You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ra...@apache.org on 2021/06/01 15:43:15 UTC

[fineract-cn-mobile] 01/02: initialize couchbaseLite, implement group & customers and add UI tests

This is an automated email from the ASF dual-hosted git repository.

rajanmaurya154 pushed a commit to branch development
in repository https://gitbox.apache.org/repos/asf/fineract-cn-mobile.git

commit 86c910ab44f65ca8fed3e19689d4924334bd533b
Author: jawidMuhammadi <sp...@gmail.com>
AuthorDate: Fri Aug 7 14:49:14 2020 +0530

    initialize couchbaseLite, implement group & customers  and add UI tests
---
 app/build.gradle                                   |  10 +-
 .../creategroup/CreateGroupActivityAndroidTest.kt  | 115 +++++++++++++++++
 .../grouplist/GroupListFragmentAndroidTest.kt      |  54 ++++++++
 .../org/apache/fineract/FineractApplication.java   |   2 +
 .../apache/fineract/couchbase/CouchbaseDatabase.kt |  72 +++++++++++
 .../org/apache/fineract/couchbase/DocumentType.kt  |  10 ++
 .../org/apache/fineract/couchbase/Replicate.java   |  69 +++++++++++
 .../fineract/couchbase/SynchronizationManager.kt   | 112 +++++++++++++++++
 .../java/org/apache/fineract/data/models/Group.kt  |   6 +-
 .../fineract/data/models/customer/Customer.kt      |  46 +++----
 .../org/apache/fineract/data/remote/BaseUrl.java   |   1 +
 .../apache/fineract/ui/adapters/GroupsAdapter.kt   |   4 +-
 .../customeractivity/CreateCustomerPresenter.java  |  89 +++++---------
 .../FormCustomerAddressFragment.java               |  18 +--
 .../customerdetails/CustomerDetailsFragment.java   |  31 +++--
 .../customerdetails/CustomerDetailsPresenter.java  |  50 ++------
 .../customers/customerlist/CustomersFragment.java  |  35 +++---
 .../customers/customerlist/CustomersPresenter.java | 136 +++++++++------------
 .../CustomerTasksBottomSheetContract.java          |   5 +-
 .../CustomerTasksBottomSheetFragment.java          |  20 ++-
 .../CustomerTasksBottomSheetPresenter.java         |  70 ++++++-----
 .../groups/creategroup/CreateGroupActivity.kt      |   6 +-
 .../online/groups/grouplist/GroupListFragment.kt   |  24 ++--
 .../ui/online/groups/grouplist/GroupViewModel.kt   |  64 +++++++---
 .../groups/grouplist/GroupViewModelFactory.kt      |  11 +-
 .../grouptasks/GroupTasksBottomSheetFragment.kt    |   4 +-
 .../java/org/apache/fineract/utils/Constants.kt    |   4 +
 .../java/org/apache/fineract/utils/DateUtils.java  |   3 +-
 .../java/org/apache/fineract/utils/GsonUtils.kt    |  31 +++++
 app/src/main/resources/groups.json                 |   3 +-
 .../fineract/online/CustomerPresenterTest.kt       |  97 ---------------
 app/sync-gateway-config.json                       |  19 +++
 build.gradle                                       |   8 +-
 33 files changed, 817 insertions(+), 412 deletions(-)

diff --git a/app/build.gradle b/app/build.gradle
index 0c7f303..5dbcc83 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -71,6 +71,9 @@ android {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
     }
+    kotlinOptions {
+        jvmTarget = JavaVersion.VERSION_1_8
+    }
 }
 
 dependencies {
@@ -157,6 +160,9 @@ dependencies {
     implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines"
     implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines"
 
+    //couchbase
+    implementation "com.couchbase.lite:couchbase-lite-android:$couchbase"
+
 
     // Instrumentation test dependencies
     androidTestImplementation jUnit
@@ -173,6 +179,9 @@ dependencies {
     androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
     androidTestImplementation "androidx.test:runner:$runnerVersion"
     androidTestImplementation "androidx.test:rules:$rulesVersion"
+    androidTestImplementation "androidx.test.ext:junit:$extJunit"
+    androidTestImplementation "androidx.test.espresso:espresso-core:$espressoCore"
+    androidTestImplementation "androidx.test.espresso:espresso-contrib: $espressonContribute"
 
     // Unit tests dependencies
     testImplementation jUnit
@@ -182,7 +191,6 @@ dependencies {
     testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion"
     testImplementation "org.hamcrest:hamcrest-integration:$hamcrestVersion"
     testImplementation "org.robolectric:robolectric:$roboElectricVersion"
-
 }
 
 // Log out test results to console
diff --git a/app/src/androidTest/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivityAndroidTest.kt b/app/src/androidTest/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivityAndroidTest.kt
new file mode 100644
index 0000000..83441ad
--- /dev/null
+++ b/app/src/androidTest/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivityAndroidTest.kt
@@ -0,0 +1,115 @@
+package org.apache.fineract.ui.online.groups.creategroup
+
+import android.content.Intent
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.action.ViewActions.typeText
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.rule.ActivityTestRule
+import org.apache.fineract.R
+import org.apache.fineract.couchbase.SynchronizationManager
+import org.apache.fineract.data.models.Group
+import org.apache.fineract.ui.online.groups.GroupAction
+import org.apache.fineract.utils.Constants
+import org.apache.fineract.utils.toDataClass
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Created by Ahmad Jawid Muhammadi on 24/8/20.
+ */
+
+@RunWith(AndroidJUnit4::class)
+class CreateGroupActivityAndroidTest {
+
+    @get:Rule
+    var activityTestRule =
+            ActivityTestRule<CreateGroupActivity>(CreateGroupActivity::class.java, false, false)
+
+    lateinit var synchronizationManager: SynchronizationManager
+
+    @Before
+    fun before() {
+        //Open CreateGroupActivity with an intent by putting  create group action as an extra
+        val intent = Intent().apply {
+            putExtra(Constants.GROUP_ACTION, GroupAction.CREATE)
+        }
+        activityTestRule.launchActivity(intent)
+        synchronizationManager = SynchronizationManager(
+                InstrumentationRegistry.getInstrumentation().context
+        )
+    }
+
+    @Test
+    fun createGroupItem() {
+        onView(withId(R.id.etIdentifier))
+                .perform(typeText("testIdentifier"))
+        onView(withId(R.id.etGroupDefinitionIdentifier))
+                .perform(typeText("group definition"))
+        onView(withId(R.id.etName))
+                .perform(typeText("group name"))
+        onView(withId(R.id.etOffice))
+                .perform(typeText("office name"))
+        onView(withId(R.id.etAssignedEmployee))
+                .perform(typeText("assignedEmployee"))
+
+        //go to next fragment
+        onView(withText("NEXT")).perform(click())
+
+        //Add a member
+        onView(withId(R.id.ibAddMember))
+                .perform(click())
+        onView(withId(R.id.etNewMember))
+                .perform(typeText("Ahmad"))
+        onView(withId((R.id.btnAddMember)))
+                .perform(click())
+        onView(withText("NEXT")).perform(click())
+
+        //Add leader name
+        onView(withId(R.id.ibAddLeader))
+                .perform(click())
+        onView(withId(R.id.etNewLeader))
+                .perform(typeText("Jawid"))
+        onView(withId((R.id.btnAddLeader)))
+                .perform(click())
+        onView(withText("NEXT")).perform(click())
+
+        //fill address details
+        onView(withId(R.id.etStreet))
+                .perform(typeText("Street"))
+        onView(withId(R.id.etCity))
+                .perform(typeText("Pune"))
+        onView(withId(R.id.etRegion))
+                .perform(typeText("Region"))
+        onView(withId(R.id.etPostalCode))
+                .perform(typeText("411048"))
+        onView(withId(R.id.etCountry))
+                .perform(typeText("India"))
+        onView(withText("NEXT")).perform(click())
+        onView(withText("COMPLETE")).perform(click())
+
+        //Then assert if group item has been created
+        val mapItem = synchronizationManager.getDocumentForTest(
+                "testIdentifier",
+                InstrumentationRegistry.getInstrumentation().context
+        )
+        val groupItem = mapItem.toDataClass<Group>()
+        assertEquals(groupItem.identifier, "testIdentifier")
+        assertEquals(groupItem.name, "group name")
+    }
+
+    @After
+    fun after() {
+        //delete the created document in test
+        synchronizationManager.deleteDocument(
+                "testIdentifier"
+        )
+    }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragmentAndroidTest.kt b/app/src/androidTest/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragmentAndroidTest.kt
new file mode 100644
index 0000000..88cc97b
--- /dev/null
+++ b/app/src/androidTest/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragmentAndroidTest.kt
@@ -0,0 +1,54 @@
+package org.apache.fineract.ui.online.groups.grouplist
+
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.contrib.DrawerActions
+import androidx.test.espresso.contrib.NavigationViewActions
+import androidx.test.espresso.contrib.RecyclerViewActions
+import androidx.test.espresso.matcher.ViewMatchers.*
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.rule.ActivityTestRule
+import org.apache.fineract.R
+import org.apache.fineract.ui.adapters.GroupsAdapter
+import org.apache.fineract.ui.online.DashboardActivity
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Created by Ahmad Jawid Muhammadi on 24/8/20.
+ */
+
+@RunWith(AndroidJUnit4::class)
+class GroupListFragmentAndroidTest {
+
+    @get:Rule
+    val activityRule =
+            ActivityTestRule<DashboardActivity>(DashboardActivity::class.java)
+
+    @Test
+    fun openDrawer_OpenGroupList_ClickOnRecyclerViewItem() {
+
+        //Open drawer
+        onView(withId(R.id.drawer_layout))
+                .perform(DrawerActions.open())
+
+        //From NavigationView navigate to Group list screen
+        onView(withId(R.id.nav_view))
+                .perform(NavigationViewActions.navigateTo(R.id.item_groups))
+
+        //Click on group item
+        onView(withId(R.id.rvGroups)).perform(
+                RecyclerViewActions.actionOnItem<GroupsAdapter.ViewHolder>(
+                        hasDescendant(withText("group")),
+                        click()
+                )
+        )
+
+        //Assert if thw item has been displayed correctly
+        onView(withId(R.id.tvIdentifier))
+                .check(matches(withText("group")))
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/apache/fineract/FineractApplication.java b/app/src/main/java/org/apache/fineract/FineractApplication.java
index de32205..8144d4f 100644
--- a/app/src/main/java/org/apache/fineract/FineractApplication.java
+++ b/app/src/main/java/org/apache/fineract/FineractApplication.java
@@ -3,6 +3,7 @@ package org.apache.fineract;
 import android.app.Application;
 import android.content.Context;
 
+import com.couchbase.lite.CouchbaseLite;
 import com.crashlytics.android.Crashlytics;
 import com.evernote.android.job.JobManager;
 import com.mifos.mobile.passcode.utils.ForegroundChecker;
@@ -37,6 +38,7 @@ public class FineractApplication extends Application {
         Fabric.with(this, new Crashlytics());
         FlowManager.init(this);
         ForegroundChecker.init(this);
+        CouchbaseLite.init(this);
     }
 
     public static Context getContext() {
diff --git a/app/src/main/java/org/apache/fineract/couchbase/CouchbaseDatabase.kt b/app/src/main/java/org/apache/fineract/couchbase/CouchbaseDatabase.kt
new file mode 100644
index 0000000..5cfac7b
--- /dev/null
+++ b/app/src/main/java/org/apache/fineract/couchbase/CouchbaseDatabase.kt
@@ -0,0 +1,72 @@
+package org.apache.fineract.couchbase
+
+import android.content.Context
+import com.couchbase.lite.CouchbaseLiteException
+import com.couchbase.lite.Database
+import com.couchbase.lite.DatabaseConfiguration
+import com.couchbase.lite.ListenerToken
+import org.apache.fineract.data.local.PreferencesHelper
+import org.apache.fineract.injection.ApplicationContext
+import org.apache.fineract.utils.Constants.DATABASE_NAME
+import javax.inject.Inject
+
+/**
+ * Created by Ahmad Jawid Muhammadi on 7/8/20.
+ */
+
+class CouchbaseDatabase @Inject constructor(
+        @ApplicationContext val context: Context?) {
+
+    @Volatile
+    private var database: Database? = null
+
+    @Inject
+    lateinit var preferencesHelper: PreferencesHelper
+
+    private var listener: ListenerToken? = null
+
+    fun getDatabase(): Database {
+        // we need this check only when we use database for UI/Integration testing
+        if (!::preferencesHelper.isInitialized) {
+            preferencesHelper = PreferencesHelper(context)
+            preferencesHelper.putUserName("ahmad")
+        }
+        return database
+                ?: createDatabase(context, preferencesHelper.userName, DATABASE_NAME).also {
+                    database = it
+                    Replicate.startReplicating(it)
+                }
+    }
+
+    fun deleteDatabase() {
+        database?.close()
+        database?.delete()
+        database = null
+    }
+
+    private fun createDatabase(
+            context: Context?,
+            userName: String,
+            databaseName: String
+    ): Database {
+        val configuration = DatabaseConfiguration()
+//        configuration.directory = String.format(
+//                "%s/%s",
+//                context?.filesDir,
+//                userName
+       // )
+
+        return Database(databaseName, configuration)
+    }
+
+    fun closeDatabaseForUser() {
+        try {
+            database?.let {
+                database?.close()
+                database = null
+            }
+        } catch (e: CouchbaseLiteException) {
+            e.printStackTrace()
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt b/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt
new file mode 100644
index 0000000..9e32fd4
--- /dev/null
+++ b/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt
@@ -0,0 +1,10 @@
+package org.apache.fineract.couchbase
+
+/**
+ * Created by Ahmad Jawid Muhammadi on 14/8/20.
+ */
+
+enum class DocumentType(val value: String) {
+    GROUP("Group"),
+    CUSTOMER("customer")
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/apache/fineract/couchbase/Replicate.java b/app/src/main/java/org/apache/fineract/couchbase/Replicate.java
new file mode 100644
index 0000000..ab61639
--- /dev/null
+++ b/app/src/main/java/org/apache/fineract/couchbase/Replicate.java
@@ -0,0 +1,69 @@
+package org.apache.fineract.couchbase;
+
+import android.util.Log;
+
+import com.couchbase.lite.BasicAuthenticator;
+import com.couchbase.lite.Database;
+import com.couchbase.lite.Endpoint;
+import com.couchbase.lite.Replicator;
+import com.couchbase.lite.ReplicatorConfiguration;
+import com.couchbase.lite.URLEndpoint;
+import com.google.gson.Gson;
+
+import org.apache.fineract.data.local.PreferencesHelper;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import static com.couchbase.lite.ReplicatorConfiguration.ReplicatorType;
+import static org.apache.fineract.data.remote.BaseUrl.LOCALHOST_URL;
+import static org.apache.fineract.utils.Constants.BASIC_AUTH_KEY;
+
+/**
+ * Created by Ahmad Jawid Muhammadi on 12/8/20.
+ */
+
+public class Replicate {
+
+    public static final String TAG = Replicate.class.getSimpleName();
+
+    public static void startReplicating(Database database) throws URISyntaxException {
+
+        Endpoint targetEndpoint = new URLEndpoint(URI.create(LOCALHOST_URL));
+        ReplicatorConfiguration config = new ReplicatorConfiguration(database, targetEndpoint);
+        config.setReplicatorType(ReplicatorType.PUSH_AND_PULL);
+
+       // config.setAuthenticator(new BasicAuthenticator(GATEWAY_USER_NAME, GATEWAY_PASSWORD));
+
+        Replicator replicator = new Replicator(config);
+
+        replicator.addChangeListener(change -> {
+            if (change.getStatus().getError() != null) {
+                Log.e(TAG, "Error status:  " + change.getStatus());
+                Log.e(TAG, "Error message:  " + change.getStatus().getProgress());
+            }
+        });
+
+        replicator.start();
+    }
+
+    /**
+     * Saves the Basic Authentication in SharedPreference
+     */
+    public void saveBasicAuthentication(
+            String username, String password,
+            PreferencesHelper preferencesHelper) {
+        preferencesHelper.putString(
+                BASIC_AUTH_KEY,
+                new Gson().toJson(new BasicAuthenticator(username, password))
+        );
+    }
+
+    public BasicAuthenticator getBasicAuthentication(
+            PreferencesHelper preferencesHelper) {
+        return new Gson().fromJson(
+                preferencesHelper.getString(BASIC_AUTH_KEY, null),
+                BasicAuthenticator.class
+        );
+    }
+}
diff --git a/app/src/main/java/org/apache/fineract/couchbase/SynchronizationManager.kt b/app/src/main/java/org/apache/fineract/couchbase/SynchronizationManager.kt
new file mode 100644
index 0000000..35f0382
--- /dev/null
+++ b/app/src/main/java/org/apache/fineract/couchbase/SynchronizationManager.kt
@@ -0,0 +1,112 @@
+package org.apache.fineract.couchbase
+
+import android.content.Context
+import android.util.Log.e
+import com.couchbase.lite.*
+import org.apache.fineract.injection.ApplicationContext
+import org.apache.fineract.utils.Constants.DATABASE_NAME
+import javax.inject.Inject
+
+
+/**
+ * Created by Ahmad Jawid Muhammadi on 6/8/20.
+ */
+
+class SynchronizationManager @Inject constructor(
+        @ApplicationContext val context: Context
+) {
+
+    private val TAG = SynchronizationManager::class.java.simpleName
+
+    @Inject
+    lateinit var database: CouchbaseDatabase
+
+    fun closeDatabase() {
+        database.closeDatabaseForUser()
+    }
+
+
+    @Throws(CouchbaseLiteException::class)
+    fun <T> saveDocument(identifier: String, properties: Map<String, T>) {
+        val document = MutableDocument(identifier, properties)
+        try {
+            e(TAG, "Document: $document")
+            database.getDatabase().save(document)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    fun deleteDocument(identifier: String) {
+        try {
+            database.getDatabase().purge(identifier)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    fun <T> updateDocument(identifier: String, properties: Map<String, T>) {
+        val document = MutableDocument(identifier, properties)
+        try {
+            database.getDatabase().save(document)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    fun getDocuments(expression: Expression, limit: Int = 50, offset: Int = 0): List<HashMap<String, Any>>? {
+        val list = arrayListOf<HashMap<String, Any>>()
+        val query = QueryBuilder
+                .select(SelectResult.all())
+                .from(DataSource.database(database.getDatabase()))
+                .where(expression)
+                .orderBy(Ordering.expression(Meta.id))
+                .limit(Expression.intValue(limit),
+                        Expression.intValue(offset)
+                )
+
+        val resultSet = query.execute()
+        var result: Result? = resultSet.next()
+        while (result != null) {
+            val valueMap = result.getDictionary(DATABASE_NAME)
+            val item = valueMap?.toMap()
+            list.add(item as HashMap<String, Any>)
+            result = resultSet.next()
+        }
+
+        return list
+    }
+
+
+    fun getDocumentForTest(identifier: String, context: Context): HashMap<String, Any> {
+        database = CouchbaseDatabase(context)
+        return getDocumentById(identifier)
+    }
+
+    fun getDocumentById(identifier: String): HashMap<String, Any> {
+        val query = QueryBuilder
+                .select(SelectResult.all())
+                .from(DataSource.database(database.getDatabase()))
+                .where(Meta.id.equalTo(Expression.string(identifier))
+                )
+                .limit(Expression.intValue(1))
+
+        val resultSet = query.execute().next()
+        return resultSet.getDictionary(DATABASE_NAME)?.toMap() as HashMap<String, Any>
+    }
+
+
+    fun clearDatabase() {
+        val query = QueryBuilder
+                .select(SelectResult.all())
+                .from(DataSource.database(database.getDatabase()))
+
+        val resultSet = query.execute()
+        var result: Result? = resultSet.next()
+        while (result != null) {
+            val valueMap = result.getDictionary(DATABASE_NAME)
+            TODO("implement code here to delete item")
+            result = resultSet.next()
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/apache/fineract/data/models/Group.kt b/app/src/main/java/org/apache/fineract/data/models/Group.kt
index 9f8a35f..781b6c0 100644
--- a/app/src/main/java/org/apache/fineract/data/models/Group.kt
+++ b/app/src/main/java/org/apache/fineract/data/models/Group.kt
@@ -2,6 +2,7 @@ package org.apache.fineract.data.models
 
 import android.os.Parcelable
 import kotlinx.android.parcel.Parcelize
+import org.apache.fineract.couchbase.DocumentType
 import org.apache.fineract.data.models.customer.Address
 
 /*
@@ -18,12 +19,13 @@ data class Group(
         var office: String? = null,
         var assignedEmployee: String? = null,
         var weekday: Int? = null,
-        var status: Status? = null,
+        var status: Status? = Status.PENDING,
         var address: Address? = null,
         var createdOn: String? = null,
         var createdBy: String? = null,
         var lastModifiedBy: String? = null,
-        var lastModifiedOn: String? = null) : Parcelable {
+        var lastModifiedOn: String? = null,
+        var documentType: String = DocumentType.GROUP.value) : Parcelable {
 
     enum class Status {
         PENDING,
diff --git a/app/src/main/java/org/apache/fineract/data/models/customer/Customer.kt b/app/src/main/java/org/apache/fineract/data/models/customer/Customer.kt
index 8fe5dd3..7471ec6 100644
--- a/app/src/main/java/org/apache/fineract/data/models/customer/Customer.kt
+++ b/app/src/main/java/org/apache/fineract/data/models/customer/Customer.kt
@@ -7,38 +7,40 @@ import com.raizlabs.android.dbflow.annotation.*
 import com.raizlabs.android.dbflow.sql.language.SQLite.select
 import com.raizlabs.android.dbflow.structure.BaseModel
 import kotlinx.android.parcel.Parcelize
+import org.apache.fineract.couchbase.DocumentType
 import org.apache.fineract.data.local.database.AppDatabase
 
 @Parcelize
 @Table(database = AppDatabase::class, useBooleanGetterSetters = false)
 data class Customer(
-    @PrimaryKey
-    @Column var identifier: String? = null,
-    @Column var type: String? = null,
-    @Column var givenName: String? = null,
-    @Column var middleName: String? = null,
-    @Column var surname: String? = null,
-    @ForeignKey(saveForeignKeyModel = true)
-    @Column var dateOfBirth: DateOfBirth? = null,
-    @Column var member: Boolean? = null,
-    @Column var accountBeneficiary: String? = null,
-    @Column var referenceCustomer: String? = null,
-    @Column var assignedOffice: String? = null,
-    @Column var assignedEmployee: String? = null,
-    @ForeignKey(saveForeignKeyModel = true)
-    @Column var address: Address? = null,
-    var contactDetails: List<ContactDetail>? = null,
-    @Column var currentState: State? = null,
-    @Column var createdBy: String? = null,
-    @Column var createdOn: String? = null,
-    @Column var lastModifiedBy: String? = null,
-    @Column var lastModifiedOn: String? = null
+        @PrimaryKey
+        @Column var identifier: String? = null,
+        @Column var type: String? = null,
+        @Column var givenName: String? = null,
+        @Column var middleName: String? = null,
+        @Column var surname: String? = null,
+        @ForeignKey(saveForeignKeyModel = true)
+        @Column var dateOfBirth: DateOfBirth? = null,
+        @Column var member: Boolean? = null,
+        @Column var accountBeneficiary: String? = null,
+        @Column var referenceCustomer: String? = null,
+        @Column var assignedOffice: String? = null,
+        @Column var assignedEmployee: String? = null,
+        @ForeignKey(saveForeignKeyModel = true)
+        @Column var address: Address? = null,
+        var contactDetails: List<ContactDetail>? = null,
+        @Column var currentState: State? = State.PENDING,
+        @Column var createdBy: String? = null,
+        @Column var createdOn: String? = null,
+        @Column var lastModifiedBy: String? = null,
+        @Column var lastModifiedOn: String? = null,
+        @Column var documentType: String = DocumentType.CUSTOMER.value
 ) : BaseModel(), Parcelable {
 
     var isUpdate: Boolean? = null
 
     @OneToMany(methods = arrayOf(OneToMany.Method.ALL), variableName = "contactDetails")
-    fun getContactDetail() : List<ContactDetail>? {
+    fun getContactDetail(): List<ContactDetail>? {
         contactDetails = select()
                 .from(ContactDetail::class.java)
                 .where(ContactDetail_Table.customer_identifier.eq(identifier))
diff --git a/app/src/main/java/org/apache/fineract/data/remote/BaseUrl.java b/app/src/main/java/org/apache/fineract/data/remote/BaseUrl.java
index 95fee4b..445bed3 100755
--- a/app/src/main/java/org/apache/fineract/data/remote/BaseUrl.java
+++ b/app/src/main/java/org/apache/fineract/data/remote/BaseUrl.java
@@ -14,6 +14,7 @@ public class BaseUrl {
     public static final String API_ENDPOINT = "pilot.kuelap.io";
     public static final String PORT = "80";
     // "/" in the last of the base url always
+    public static final String LOCALHOST_URL = "ws://10.0.2.2:4984/fineract-cn/";
 
     public String getName() {
         return "fineract";
diff --git a/app/src/main/java/org/apache/fineract/ui/adapters/GroupsAdapter.kt b/app/src/main/java/org/apache/fineract/ui/adapters/GroupsAdapter.kt
index 75e6ece..0324bf2 100644
--- a/app/src/main/java/org/apache/fineract/ui/adapters/GroupsAdapter.kt
+++ b/app/src/main/java/org/apache/fineract/ui/adapters/GroupsAdapter.kt
@@ -37,7 +37,9 @@ class GroupsAdapter @Inject constructor(@ApplicationContext var context: Context
     override fun onBindViewHolder(holder: ViewHolder, position: Int) {
         var group: Group = groups[position]
 
-        StatusUtils.setGroupsStatusIcon(group.status, holder.ivTypeIndicator, context)
+        group.status?.let {
+            StatusUtils.setGroupsStatusIcon(group.status, holder.ivTypeIndicator, context)
+        }
 
         holder.tvGroupIdentifier.text = group.identifier
 
diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/customeractivity/CreateCustomerPresenter.java b/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/customeractivity/CreateCustomerPresenter.java
index 251a6e1..a09bad3 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/customeractivity/CreateCustomerPresenter.java
+++ b/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/customeractivity/CreateCustomerPresenter.java
@@ -1,87 +1,64 @@
 package org.apache.fineract.ui.online.customers.createcustomer.customeractivity;
 
 import android.content.Context;
+import android.util.Log;
 
-import org.apache.fineract.R;
-import org.apache.fineract.data.datamanager.contracts.ManagerCustomer;
-import org.apache.fineract.data.datamanager.database.DbManagerCustomer;
+import com.couchbase.lite.CouchbaseLiteException;
+
+import org.apache.fineract.couchbase.SynchronizationManager;
+import org.apache.fineract.data.local.PreferencesHelper;
 import org.apache.fineract.data.models.customer.Customer;
 import org.apache.fineract.injection.ApplicationContext;
 import org.apache.fineract.injection.ConfigPersistent;
 import org.apache.fineract.ui.base.BasePresenter;
+import org.apache.fineract.utils.DateUtils;
+import org.apache.fineract.utils.GsonUtilsKt;
 
-import javax.inject.Inject;
+import java.util.Objects;
 
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.CompositeDisposable;
-import io.reactivex.observers.DisposableCompletableObserver;
-import io.reactivex.schedulers.Schedulers;
+import javax.inject.Inject;
 
 /**
  * @author Rajan Maurya
- *         On 27/07/17.
+ * On 27/07/17.
  */
 @ConfigPersistent
 public class CreateCustomerPresenter extends BasePresenter<CreateCustomerContract.View>
         implements CreateCustomerContract.Presenter {
 
-    private ManagerCustomer dbManagerCustomer;
-    private final CompositeDisposable compositeDisposable;
+    private SynchronizationManager synchronizationManager;
+    private PreferencesHelper preferencesHelper;
 
     @Inject
     public CreateCustomerPresenter(@ApplicationContext Context context,
-            DbManagerCustomer dataManagerCustomer) {
+                                   SynchronizationManager synchronizationManager,
+                                   PreferencesHelper preferencesHelper) {
         super(context);
-        this.dbManagerCustomer = dataManagerCustomer;
-        compositeDisposable = new CompositeDisposable();
+        this.synchronizationManager = synchronizationManager;
+        this.preferencesHelper = preferencesHelper;
     }
 
     @Override
-    public void createCustomer(final Customer customer) {
-        checkViewAttached();
-        getMvpView().showProgressbar();
-        compositeDisposable.add(dbManagerCustomer.createCustomer(customer)
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribeWith(new DisposableCompletableObserver() {
-                    @Override
-                    public void onComplete() {
-                        getMvpView().hideProgressbar();
-                        getMvpView().customerCreatedSuccessfully();
-                    }
-
-                    @Override
-                    public void onError(Throwable throwable) {
-                        getMvpView().hideProgressbar();
-                        showExceptionError(throwable,
-                                context.getString(R.string.error_creating_customer));
-                    }
-                })
-
-        );
+    public void createCustomer(Customer customer) {
+        try {
+            customer.setCreatedBy(preferencesHelper.getUserName());
+            customer.setCreatedOn(DateUtils.getCurrentDate());
+            customer.setLastModifiedBy(preferencesHelper.getUserName());
+            customer.setLastModifiedOn(DateUtils.getCurrentDate());
+            synchronizationManager.saveDocument(Objects.requireNonNull(customer.getIdentifier()),
+                    GsonUtilsKt.serializeToMap(customer));
+        } catch (CouchbaseLiteException e) {
+            Log.e("CreateCustomerPresenter", e.toString());
+        }
+        getMvpView().customerCreatedSuccessfully();
     }
 
     @Override
     public void updateCustomer(String customerIdentifier, Customer customer) {
-        checkViewAttached();
-        getMvpView().showProgressbar();
-        compositeDisposable.add(dbManagerCustomer.updateCustomer(customerIdentifier, customer)
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribeWith(new DisposableCompletableObserver() {
-                    @Override
-                    public void onComplete() {
-                        getMvpView().hideProgressbar();
-                        getMvpView().customerUpdatedSuccessfully();
-                    }
-
-                    @Override
-                    public void onError(Throwable throwable) {
-                        getMvpView().hideProgressbar();
-                        showExceptionError(throwable,
-                                context.getString(R.string.error_updating_customer));
-                    }
-                })
-        );
+        customer.setLastModifiedBy(preferencesHelper.getUserName());
+        customer.setLastModifiedOn(DateUtils.getCurrentDate());
+        synchronizationManager.updateDocument(Objects.requireNonNull(customer.getIdentifier()),
+                GsonUtilsKt.serializeToMap(customer));
+        getMvpView().customerUpdatedSuccessfully();
     }
 }
diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/formcustomeraddress/FormCustomerAddressFragment.java b/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/formcustomeraddress/FormCustomerAddressFragment.java
index f3ba64c..b4c446f 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/formcustomeraddress/FormCustomerAddressFragment.java
+++ b/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/formcustomeraddress/FormCustomerAddressFragment.java
@@ -134,16 +134,18 @@ public class FormCustomerAddressFragment extends FineractBaseFragment implements
 
     public void showPreviousAddress() {
         Address address = customer.getAddress();
-        etStreet.setText(address.getStreet());
-        etCity.setText(address.getCity());
-        if (address.getPostalCode() != null) {
-            etPostalCode.setText(address.getPostalCode());
+        if (address != null) {
+            etStreet.setText(address.getStreet());
+            etCity.setText(address.getCity());
+            if (address.getPostalCode() != null) {
+                etPostalCode.setText(address.getPostalCode());
+            }
+            etCountry.setText(address.getCountry());
+            if (address.getRegion() != null) {
+                etRegion.setText(address.getRegion());
+            }
         }
-        etCountry.setText(address.getCountry());
         showTextInputLayoutError(tilCountry, null);
-        if (address.getRegion() != null) {
-            etRegion.setText(address.getRegion());
-        }
     }
 
     @Override
diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsFragment.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsFragment.java
index b34f58e..64cdfe0 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsFragment.java
+++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsFragment.java
@@ -2,12 +2,6 @@ package org.apache.fineract.ui.online.customers.customerdetails;
 
 import android.content.Intent;
 import android.os.Bundle;
-import androidx.annotation.Nullable;
-import com.google.android.material.appbar.AppBarLayout;
-import com.google.android.material.appbar.CollapsingToolbarLayout;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -15,6 +9,14 @@ import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+
+import com.google.android.material.appbar.AppBarLayout;
+import com.google.android.material.appbar.CollapsingToolbarLayout;
+
 import org.apache.fineract.R;
 import org.apache.fineract.data.models.customer.Address;
 import org.apache.fineract.data.models.customer.ContactDetail;
@@ -44,7 +46,7 @@ import butterknife.OnClick;
 
 /**
  * @author Rajan Maurya
- *         On 26/06/17.
+ * On 26/06/17.
  */
 public class CustomerDetailsFragment extends FineractBaseFragment
         implements AppBarLayout.OnOffsetChangedListener, CustomerDetailsContract.View,
@@ -133,7 +135,7 @@ public class CustomerDetailsFragment extends FineractBaseFragment
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
-            @Nullable Bundle savedInstanceState) {
+                             @Nullable Bundle savedInstanceState) {
         rootView = inflater.inflate(R.layout.fragment_customer_details, container, false);
         ((FineractBaseActivity) getActivity()).getActivityComponent().inject(this);
         ButterKnife.bind(this, rootView);
@@ -181,6 +183,7 @@ public class CustomerDetailsFragment extends FineractBaseFragment
         tasksBottomSheet.setCustomerIdentifier(customerIdentifier);
         tasksBottomSheet.setCustomerTasksChangeListener(this);
         tasksBottomSheet.show(getChildFragmentManager(), getString(R.string.tasks));
+        tasksBottomSheet.setCustomer(customer);
     }
 
     @OnClick(R.id.ll_identifier_cards)
@@ -246,14 +249,16 @@ public class CustomerDetailsFragment extends FineractBaseFragment
 
         Address address = customer.getAddress();
         StringBuilder addressBuilder = new StringBuilder();
-        addressBuilder
-                .append(address.getStreet()).append(", ")
-                .append(address.getCity()).append(", ");
-        if (address.getPostalCode() != null) {
+        if (address != null) {
+            addressBuilder.append(address.getStreet()).append(", ")
+                    .append(address.getCity()).append(", ");
+
             addressBuilder.append(address.getPostalCode());
             addressBuilder.append(", ");
+
+            addressBuilder.append(address.getCountry());
         }
-        addressBuilder.append(address.getCountry());
+
         tvAddress.setText(addressBuilder);
 
         if (customer.getContactDetails().size() == 0) {
diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsPresenter.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsPresenter.java
index 97072ae..6047187 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsPresenter.java
+++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsPresenter.java
@@ -2,38 +2,32 @@ package org.apache.fineract.ui.online.customers.customerdetails;
 
 import android.content.Context;
 
-import org.apache.fineract.R;
-import org.apache.fineract.data.datamanager.contracts.ManagerCustomer;
-import org.apache.fineract.data.datamanager.database.DbManagerCustomer;
+import org.apache.fineract.couchbase.SynchronizationManager;
 import org.apache.fineract.data.models.customer.Customer;
 import org.apache.fineract.injection.ApplicationContext;
 import org.apache.fineract.injection.ConfigPersistent;
 import org.apache.fineract.ui.base.BasePresenter;
+import org.apache.fineract.utils.GsonUtilsKt;
 
-import javax.inject.Inject;
+import java.util.HashMap;
 
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.CompositeDisposable;
-import io.reactivex.observers.DisposableObserver;
-import io.reactivex.schedulers.Schedulers;
+import javax.inject.Inject;
 
 /**
  * @author Rajan Maurya
- *         On 26/06/17.
+ * On 26/06/17.
  */
 @ConfigPersistent
 public class CustomerDetailsPresenter extends BasePresenter<CustomerDetailsContract.View>
         implements CustomerDetailsContract.Presenter {
 
-    private ManagerCustomer dataManagerCustomer;
-    private final CompositeDisposable compositeDisposable;
+    private SynchronizationManager synchronizationManager;
 
     @Inject
     public CustomerDetailsPresenter(@ApplicationContext Context context,
-            DbManagerCustomer dataManager) {
+                                    SynchronizationManager synchronizationManager) {
         super(context);
-        dataManagerCustomer = dataManager;
-        compositeDisposable = new CompositeDisposable();
+        this.synchronizationManager = synchronizationManager;
     }
 
     @Override
@@ -44,35 +38,15 @@ public class CustomerDetailsPresenter extends BasePresenter<CustomerDetailsContr
     @Override
     public void detachView() {
         super.detachView();
-        compositeDisposable.clear();
     }
 
     @Override
     public void loanCustomerDetails(String identifier) {
         checkViewAttached();
         getMvpView().showProgressbar();
-        compositeDisposable.add(dataManagerCustomer.fetchCustomer(identifier)
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribeWith(new DisposableObserver<Customer>() {
-                    @Override
-                    public void onNext(Customer customer) {
-                        getMvpView().hideProgressbar();
-                        getMvpView().showCustomerDetails(customer);
-                    }
-
-                    @Override
-                    public void onError(Throwable throwable) {
-                        getMvpView().hideProgressbar();
-                        showExceptionError(throwable,
-                                context.getString(R.string.error_fetching_customer_details));
-                    }
-
-                    @Override
-                    public void onComplete() {
-
-                    }
-                })
-        );
+        HashMap<String, Object> item = synchronizationManager.getDocumentById(identifier);
+        Customer customer = GsonUtilsKt.convertToData(item, Customer.class);
+        getMvpView().showCustomerDetails(customer);
+        getMvpView().hideProgressbar();
     }
 }
diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersFragment.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersFragment.java
index b4d80d1..32a9938 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersFragment.java
+++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersFragment.java
@@ -1,18 +1,9 @@
 package org.apache.fineract.ui.online.customers.customerlist;
 
-import static android.app.Activity.RESULT_OK;
-
 import android.app.SearchManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.transition.TransitionManager;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.appcompat.widget.SearchView;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -23,6 +14,14 @@ import android.widget.LinearLayout;
 import android.widget.RadioButton;
 import android.widget.RadioGroup;
 
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.SearchView;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+import androidx.transition.TransitionManager;
+
 import com.github.therajanmaurya.sweeterror.SweetUIErrorHandler;
 
 import org.apache.fineract.R;
@@ -48,6 +47,8 @@ import butterknife.BindView;
 import butterknife.ButterKnife;
 import butterknife.OnClick;
 
+import static android.app.Activity.RESULT_OK;
+
 /**
  * @author Rajan Maurya
  * On 20/06/17.
@@ -123,18 +124,19 @@ public class CustomersFragment extends FineractBaseFragment implements Customers
 
         showUserInterface();
         sweetUIErrorHandler = new SweetUIErrorHandler(getActivity(), rootView);
-        if (preferencesHelper.isFetching()) {
-            sweetUIErrorHandler.showSweetCustomErrorUI(getString(R.string.syncing_please_wait),
-                    R.drawable.ic_error_black_24dp, swipeRefreshLayout, layoutError);
-        } else {
-            customerPresenter.fetchCustomers(0, false);
-        }
+
         return rootView;
     }
 
     @Override
     public void onResume() {
         super.onResume();
+        if (preferencesHelper.isFetching()) {
+            sweetUIErrorHandler.showSweetCustomErrorUI(getString(R.string.syncing_please_wait),
+                    R.drawable.ic_error_black_24dp, swipeRefreshLayout, layoutError);
+        } else {
+            customerPresenter.fetchCustomers(0, false);
+        }
         if (isNewCustomer) {
             isNewCustomer = false;
             customerPresenter.fetchCustomers(0, false);
@@ -192,8 +194,7 @@ public class CustomersFragment extends FineractBaseFragment implements Customers
     @Override
     public void showEmptyCustomers(String message) {
         showRecyclerView(false);
-        sweetUIErrorHandler.showSweetCustomErrorUI(getString(R.string.customer),
-                getString(Integer.parseInt(message)),
+        sweetUIErrorHandler.showSweetCustomErrorUI(message,
                 R.drawable.ic_customer_black_24dp, rvCustomers, layoutError);
     }
 
diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersPresenter.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersPresenter.java
index 398b615..afdf1b1 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersPresenter.java
+++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersPresenter.java
@@ -2,24 +2,23 @@ package org.apache.fineract.ui.online.customers.customerlist;
 
 import android.content.Context;
 
+import com.couchbase.lite.Expression;
+
 import org.apache.fineract.R;
-import org.apache.fineract.data.datamanager.contracts.ManagerCustomer;
-import org.apache.fineract.data.datamanager.database.DbManagerCustomer;
+import org.apache.fineract.couchbase.DocumentType;
+import org.apache.fineract.couchbase.SynchronizationManager;
 import org.apache.fineract.data.models.customer.Customer;
-import org.apache.fineract.data.models.customer.CustomerPage;
 import org.apache.fineract.injection.ApplicationContext;
 import org.apache.fineract.injection.ConfigPersistent;
 import org.apache.fineract.ui.base.BasePresenter;
+import org.apache.fineract.utils.GsonUtilsKt;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 import javax.inject.Inject;
 
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.CompositeDisposable;
-import io.reactivex.observers.DisposableObserver;
-import io.reactivex.schedulers.Schedulers;
-
 /**
  * @author Rajan Maurya
  * On 20/06/17.
@@ -28,18 +27,16 @@ import io.reactivex.schedulers.Schedulers;
 public class CustomersPresenter extends BasePresenter<CustomersContract.View>
         implements CustomersContract.Presenter {
 
-    private final ManagerCustomer dataManagerCustomer;
-    private CompositeDisposable compositeDisposable;
-
     private int customerListSize = 50;
     private boolean loadmore = false;
 
+    private SynchronizationManager synchronizationManager;
+
     @Inject
     public CustomersPresenter(@ApplicationContext Context context,
-                              DbManagerCustomer dataManager) {
+                              SynchronizationManager synchronizationManager) {
         super(context);
-        dataManagerCustomer = dataManager;
-        compositeDisposable = new CompositeDisposable();
+        this.synchronizationManager = synchronizationManager;
     }
 
     @Override
@@ -50,7 +47,6 @@ public class CustomersPresenter extends BasePresenter<CustomersContract.View>
     @Override
     public void detachView() {
         super.detachView();
-        compositeDisposable.clear();
     }
 
     @Override
@@ -63,44 +59,34 @@ public class CustomersPresenter extends BasePresenter<CustomersContract.View>
     public void fetchCustomers(Integer pageIndex, Integer size) {
         checkViewAttached();
         getMvpView().showProgressbar();
-        compositeDisposable.add(dataManagerCustomer.fetchCustomers(pageIndex, size)
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribeWith(new DisposableObserver<CustomerPage>() {
-                    @Override
-                    public void onNext(CustomerPage customerPage) {
-                        getMvpView().hideProgressbar();
-
-                        if (!loadmore && customerPage.getTotalPages() == 0) {
-                            getMvpView().showEmptyCustomers(
-                                    context.getString(R.string.empty_customer_list));
-                        } else if (loadmore && customerPage.getCustomers().size() == 0) {
-                            getMvpView().showMessage(
-                                    context.getString(R.string.no_more_customer_available));
-                        } else {
-                            showCustomers(customerPage.getCustomers());
-                        }
-                    }
-
-                    @Override
-                    public void onError(Throwable throwable) {
-                        getMvpView().hideProgressbar();
-                        if (loadmore) {
-                            getMvpView().showMessage(
-                                    context.getString(R.string.error_loading_customers));
-                        } else {
-                            showExceptionError(throwable,
-                                    context.getString(R.string.error_loading_customers));
-                        }
-                    }
-
-                    @Override
-                    public void onComplete() {
-                    }
-                })
-        );
+        ArrayList<Customer> customerList = new ArrayList<>();
+
+        Expression expression = Expression.property("documentType")
+                .equalTo(Expression.string(DocumentType.CUSTOMER.getValue()));
+
+        List<HashMap<String, Object>> map = synchronizationManager.getDocuments(
+                expression,
+                size,
+                pageIndex);
+
+        for (HashMap<String, Object> item : map) {
+            Customer customer = GsonUtilsKt.convertToData(item, Customer.class);
+            customerList.add(customer);
+        }
+
+        if (!loadmore && customerList.size() == 0) {
+            getMvpView().showEmptyCustomers(
+                    context.getString(R.string.empty_customer_list));
+        } else if (loadmore && customerList.size() == 0) {
+            getMvpView().showMessage(
+                    context.getString(R.string.no_more_customer_available));
+        } else {
+            showCustomers(customerList);
+        }
+        getMvpView().hideProgressbar();
     }
 
+
     @Override
     public void showCustomers(List<Customer> customers) {
         if (loadmore) {
@@ -112,29 +98,29 @@ public class CustomersPresenter extends BasePresenter<CustomersContract.View>
 
     @Override
     public void searchCustomerOnline(String query) {
-        checkViewAttached();
-        getMvpView().showProgressbar();
-        compositeDisposable.add(
-                dataManagerCustomer.fetchCustomer(query)
-                        .subscribeOn(Schedulers.io())
-                        .observeOn(AndroidSchedulers.mainThread())
-                        .subscribeWith(new DisposableObserver<Customer>() {
-                            @Override
-                            public void onNext(Customer value) {
-                                getMvpView().hideProgressbar();
-                                getMvpView().searchCustomerList(value);
-                            }
-
-                            @Override
-                            public void onError(Throwable e) {
-                                showExceptionError(e,
-                                        context.getString(R.string.error_finding_customer));
-                            }
-
-                            @Override
-                            public void onComplete() {
-
-                            }
-                        }));
+//        checkViewAttached();
+//        getMvpView().showProgressbar();
+//        compositeDisposable.add(
+//                dataManagerCustomer.fetchCustomer(query)
+//                        .subscribeOn(Schedulers.io())
+//                        .observeOn(AndroidSchedulers.mainThread())
+//                        .subscribeWith(new DisposableObserver<Customer>() {
+//                            @Override
+//                            public void onNext(Customer value) {
+//                                getMvpView().hideProgressbar();
+//                                getMvpView().searchCustomerList(value);
+//                            }
+//
+//                            @Override
+//                            public void onError(Throwable e) {
+//                                showExceptionError(e,
+//                                        context.getString(R.string.error_finding_customer));
+//                            }
+//
+//                            @Override
+//                            public void onComplete() {
+//
+//                            }
+//                        }));
     }
 }
diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetContract.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetContract.java
index 9d52907..7e79fe6 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetContract.java
+++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetContract.java
@@ -1,11 +1,12 @@
 package org.apache.fineract.ui.online.customers.customertasks;
 
 import org.apache.fineract.data.models.customer.Command;
+import org.apache.fineract.data.models.customer.Customer;
 import org.apache.fineract.ui.base.MvpView;
 
 /**
  * @author Rajan Maurya
- *         On 27/07/17.
+ * On 27/07/17.
  */
 
 public interface CustomerTasksBottomSheetContract {
@@ -21,6 +22,6 @@ public interface CustomerTasksBottomSheetContract {
 
     interface Presenter {
 
-        void changeCustomerStatus(String identifier, Command command);
+        void changeCustomerStatus(String identifier, Customer customer, Command command);
     }
 }
diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetFragment.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetFragment.java
index 65e99d7..c09ca25 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetFragment.java
+++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetFragment.java
@@ -3,10 +3,6 @@ package org.apache.fineract.ui.online.customers.customertasks;
 import android.app.Dialog;
 import android.content.DialogInterface;
 import android.os.Bundle;
-import androidx.annotation.NonNull;
-import com.google.android.material.bottomsheet.BottomSheetBehavior;
-import com.google.android.material.bottomsheet.BottomSheetDialog;
-import androidx.core.content.ContextCompat;
 import android.view.View;
 import android.widget.Button;
 import android.widget.EditText;
@@ -15,6 +11,12 @@ import android.widget.LinearLayout;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+
 import org.apache.fineract.R;
 import org.apache.fineract.data.models.customer.Command;
 import org.apache.fineract.data.models.customer.Customer;
@@ -30,7 +32,7 @@ import butterknife.OnClick;
 
 /**
  * @author Rajan Maurya
- *         On 27/07/17.
+ * On 27/07/17.
  */
 public class CustomerTasksBottomSheetFragment extends FineractBaseBottomSheetDialogFragment
         implements CustomerTasksBottomSheetContract.View {
@@ -71,6 +73,7 @@ public class CustomerTasksBottomSheetFragment extends FineractBaseBottomSheetDia
     @Inject
     CustomerTasksBottomSheetPresenter tasksBottomSheetPresenter;
 
+
     View rootView;
 
     private BottomSheetBehavior behavior;
@@ -78,6 +81,7 @@ public class CustomerTasksBottomSheetFragment extends FineractBaseBottomSheetDia
     private Command command;
     private String customerIdentifier;
     private OnTasksChangeListener onTasksChangeListener;
+    private Customer customer;
 
     @NonNull
     @Override
@@ -188,11 +192,15 @@ public class CustomerTasksBottomSheetFragment extends FineractBaseBottomSheetDia
         customerIdentifier = identifier;
     }
 
+    public void setCustomer(Customer customer) {
+        this.customer = customer;
+    }
+
     @OnClick(R.id.btn_submit_task)
     void submitTask() {
         command.setComment(etComment.getText().toString().trim());
         etComment.setEnabled(false);
-        tasksBottomSheetPresenter.changeCustomerStatus(customerIdentifier, command);
+        tasksBottomSheetPresenter.changeCustomerStatus(customerIdentifier, customer, command);
     }
 
     @OnClick(R.id.btn_cancel)
diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetPresenter.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetPresenter.java
index 5d3f6a7..e8cdece 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetPresenter.java
+++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetPresenter.java
@@ -1,39 +1,38 @@
 package org.apache.fineract.ui.online.customers.customertasks;
 
 import android.content.Context;
+import android.util.Log;
 
 import org.apache.fineract.R;
-import org.apache.fineract.data.datamanager.api.DataManagerCustomer;
+import org.apache.fineract.couchbase.SynchronizationManager;
 import org.apache.fineract.data.models.customer.Command;
+import org.apache.fineract.data.models.customer.Customer;
 import org.apache.fineract.injection.ApplicationContext;
 import org.apache.fineract.injection.ConfigPersistent;
 import org.apache.fineract.ui.base.BasePresenter;
+import org.apache.fineract.utils.GsonUtilsKt;
 
-import javax.inject.Inject;
+import java.util.Objects;
 
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.CompositeDisposable;
-import io.reactivex.observers.DisposableCompletableObserver;
-import io.reactivex.schedulers.Schedulers;
+import javax.inject.Inject;
 
 /**
  * @author Rajan Maurya
- *         On 28/07/17.
+ * On 28/07/17.
  */
 @ConfigPersistent
 public class CustomerTasksBottomSheetPresenter
         extends BasePresenter<CustomerTasksBottomSheetContract.View>
         implements CustomerTasksBottomSheetContract.Presenter {
 
-    private DataManagerCustomer dataManagerCustomer;
-    private final CompositeDisposable compositeDisposable;
+    private SynchronizationManager synchronizationManager;
 
     @Inject
     public CustomerTasksBottomSheetPresenter(@ApplicationContext Context context,
-            DataManagerCustomer dataManagerCustomer) {
+                                             SynchronizationManager synchronizationManager) {
         super(context);
-        this.dataManagerCustomer = dataManagerCustomer;
-        compositeDisposable = new CompositeDisposable();
+
+        this.synchronizationManager = synchronizationManager;
     }
 
     @Override
@@ -44,30 +43,37 @@ public class CustomerTasksBottomSheetPresenter
     @Override
     public void detachView() {
         super.detachView();
-        compositeDisposable.clear();
+        synchronizationManager.closeDatabase();
     }
 
     @Override
-    public void changeCustomerStatus(String identifier, Command command) {
+    public void changeCustomerStatus(String identifier, Customer customer, Command command) {
         checkViewAttached();
         getMvpView().showProgressbar();
-        compositeDisposable.add(dataManagerCustomer.customerCommand(identifier, command)
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribeWith(new DisposableCompletableObserver() {
-                    @Override
-                    public void onComplete() {
-                        getMvpView().hideProgressbar();
-                        getMvpView().statusChangedSuccessfully();
-                    }
-
-                    @Override
-                    public void onError(Throwable throwable) {
-                        getMvpView().hideProgressbar();
-                        showExceptionError(throwable,
-                                context.getString(R.string.error_updating_status));
-                    }
-                })
-        );
+        try {
+            switch (Objects.requireNonNull(command.getAction())) {
+                case LOCK:
+                    customer.setCurrentState(Customer.State.LOCKED);
+                    break;
+                case REOPEN:
+                    customer.setCurrentState(Customer.State.PENDING);
+                    break;
+                case ACTIVATE:
+                case UNLOCK:
+                    customer.setCurrentState(Customer.State.ACTIVE);
+                    break;
+                case CLOSE:
+                    customer.setCurrentState(Customer.State.CLOSED);
+                    break;
+            }
+            synchronizationManager.updateDocument(identifier, GsonUtilsKt.serializeToMap(customer));
+            getMvpView().hideProgressbar();
+            getMvpView().statusChangedSuccessfully();
+        } catch (Exception e) {
+            getMvpView().hideProgressbar();
+            showExceptionError(e,
+                    context.getString(R.string.error_updating_status));
+            Log.e("CustomerTasks", e.toString());
+        }
     }
 }
diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivity.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivity.kt
index 00e5a8a..871f53f 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivity.kt
+++ b/app/src/main/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivity.kt
@@ -15,8 +15,8 @@ import org.apache.fineract.data.models.customer.Address
 import org.apache.fineract.ui.base.FineractBaseActivity
 import org.apache.fineract.ui.base.Toaster
 import org.apache.fineract.ui.online.groups.GroupAction
-import org.apache.fineract.ui.online.groups.grouplist.GroupViewModelFactory
 import org.apache.fineract.ui.online.groups.grouplist.GroupViewModel
+import org.apache.fineract.ui.online.groups.grouplist.GroupViewModelFactory
 import org.apache.fineract.utils.Constants
 import org.apache.fineract.utils.DateUtils
 import javax.inject.Inject
@@ -85,7 +85,7 @@ class CreateGroupActivity : FineractBaseActivity(), StepperLayout.StepperListene
                 Status.DONE -> {
                     hideMifosProgressDialog()
                     if (groupAction == GroupAction.CREATE) {
-                        Toast.makeText(this, getString(R.string.group_identifier_updated_successfully, group.identifier), Toast.LENGTH_SHORT).show()
+                        Toast.makeText(this, getString(R.string.group_identifier_created_successfully, group.identifier), Toast.LENGTH_SHORT).show()
                     } else {
                         Toast.makeText(this, getString(R.string.group_identifier_updated_successfully, group.identifier), Toast.LENGTH_SHORT).show()
                     }
@@ -98,7 +98,7 @@ class CreateGroupActivity : FineractBaseActivity(), StepperLayout.StepperListene
     override fun onCompleted(completeButton: View?) {
         when (groupAction) {
             GroupAction.EDIT -> group.identifier?.let {
-                viewModel.updateGroup(it, group)
+                viewModel.updateGroup(group)
             }
             GroupAction.CREATE -> viewModel.createGroup(group)
         }
diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragment.kt
index 448554b..fad1dfd 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragment.kt
+++ b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragment.kt
@@ -30,6 +30,7 @@ import javax.inject.Inject
  * Created by saksham on 15/June/2019
 */
 
+
 class GroupListFragment : FineractBaseFragment(), OnItemClickListener {
 
     lateinit var rootView: View
@@ -42,6 +43,7 @@ class GroupListFragment : FineractBaseFragment(), OnItemClickListener {
     @Inject
     lateinit var groupViewModelFactory: GroupViewModelFactory
 
+
     lateinit var groupList: ArrayList<Group>
 
     val searchedGroup: (ArrayList<Group>) -> Unit = { groups ->
@@ -63,15 +65,11 @@ class GroupListFragment : FineractBaseFragment(), OnItemClickListener {
                               savedInstanceState: Bundle?): View? {
         rootView = inflater.inflate(R.layout.fragment_group_list, container, false)
         (activity as FineractBaseActivity).activityComponent.inject(this)
-        viewModel = ViewModelProviders.of(this,
-                groupViewModelFactory).get(GroupViewModel::class.java)
+        viewModel = ViewModelProviders.of(this, groupViewModelFactory)
+                .get(GroupViewModel::class.java)
         return rootView
     }
 
-    override fun onActivityCreated(savedInstanceState: Bundle?) {
-        super.onActivityCreated(savedInstanceState)
-    }
-
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         ButterKnife.bind(this, rootView)
@@ -79,12 +77,16 @@ class GroupListFragment : FineractBaseFragment(), OnItemClickListener {
 
         rvGroups.adapter = adapter
         rvGroups.layoutManager = LinearLayoutManager(context)
-        viewModel.getGroups().observe(this,
+    }
 
-                Observer {
-                    groupList = it
-                    adapter.setGroupList(it)
-                })
+    override fun onStart() {
+        super.onStart()
+        viewModel.getGroups()?.observe(this, Observer {
+            it?.let {
+                groupList = it
+                adapter.setGroupList(it)
+            }
+        })
     }
 
     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModel.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModel.kt
index 6f9f159..8618704 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModel.kt
+++ b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModel.kt
@@ -4,43 +4,58 @@ import android.annotation.SuppressLint
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
+import com.couchbase.lite.Expression
 import io.reactivex.Observable
 import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.functions.Predicate
 import io.reactivex.schedulers.Schedulers
 import kotlinx.coroutines.*
+import org.apache.fineract.couchbase.DocumentType
+import org.apache.fineract.couchbase.SynchronizationManager
 import org.apache.fineract.data.Status
 import org.apache.fineract.data.datamanager.api.DataManagerAnonymous
-import org.apache.fineract.data.datamanager.api.DataManagerGroups
+import org.apache.fineract.data.local.PreferencesHelper
 import org.apache.fineract.data.models.Group
 import org.apache.fineract.data.models.customer.Command
 import org.apache.fineract.data.models.customer.Country
+import org.apache.fineract.utils.DateUtils
+import org.apache.fineract.utils.serializeToMap
+import org.apache.fineract.utils.toDataClass
 
 /*
  * Created by saksham on 15/June/2019
 */
 
-class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val dataManagerAnonymous: DataManagerAnonymous) : ViewModel() {
+class GroupViewModel constructor(private val synchronizationManager: SynchronizationManager,
+                                 private val dataManagerAnonymous: DataManagerAnonymous,
+                                 private val preferencesHelper: PreferencesHelper) : ViewModel() {
 
-    lateinit var groupsList: MutableLiveData<ArrayList<Group>>
+    var groupsList = MutableLiveData<ArrayList<Group>>()
     private var viewModelJob = Job()
+
     // Create a Coroutine scope using a job to be able to cancel when needed
     private val uiScope = CoroutineScope(viewModelJob + Dispatchers.IO)
     private var _status = MutableLiveData<Status>()
     val status: LiveData<Status>
         get() = _status
 
-    fun getGroups(): MutableLiveData<ArrayList<Group>> {
-        groupsList = dataManagerGroups.getGroups()
+    fun getGroups(): MutableLiveData<ArrayList<Group>>? {
+        val expression = Expression.property("documentType")
+                .equalTo(Expression.string(DocumentType.GROUP.value))
+                .and(Expression.property("status").notEqualTo(Expression.string("null")))
+        val hashMapList = synchronizationManager.getDocuments(expression)
+        if (hashMapList?.isEmpty() == null) {
+            return null
+        }
+        val list = arrayListOf<Group>()
+        for (map in hashMapList) {
+            list.add(map.toDataClass())
+        }
+        groupsList.value = list
         return groupsList
     }
 
     fun searchGroup(groups: ArrayList<Group>, query: String, searchedGroup: (ArrayList<Group>) -> Unit) {
-        searchedGroup(ArrayList(Observable.fromIterable(groups).filter(object : Predicate<Group> {
-            override fun test(group: Group): Boolean {
-                return group.identifier?.toLowerCase()?.contains(query.toLowerCase()).toString().toBoolean()
-            }
-        }).toList().blockingGet()))
+        searchedGroup(ArrayList(Observable.fromIterable(groups).filter { group -> group.identifier?.toLowerCase()?.contains(query.toLowerCase()).toString().toBoolean() }.toList().blockingGet()))
     }
 
     @SuppressLint("CheckResult")
@@ -59,7 +74,11 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d
             withContext(Dispatchers.Main) {
                 try {
                     _status.value = Status.LOADING
-                    dataManagerGroups.createGroup(group).await()
+                    group.createdBy = preferencesHelper.userName
+                    group.createdOn = DateUtils.getCurrentDate()
+                    group.lastModifiedBy = preferencesHelper.userName
+                    group.lastModifiedOn = DateUtils.getCurrentDate()
+                    synchronizationManager.saveDocument(group.identifier!!, group.serializeToMap())
                     _status.value = Status.DONE
                 } catch (e: Exception) {
                     _status.value = Status.ERROR
@@ -68,12 +87,12 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d
         }
     }
 
-    fun updateGroup(identifier: String, group: Group) {
+    fun updateGroup(group: Group) {
         uiScope.launch {
             withContext(Dispatchers.Main) {
                 try {
                     _status.value = Status.LOADING
-                    dataManagerGroups.updateGroup(identifier, group).await()
+                    synchronizationManager.updateDocument(group.identifier!!, group.serializeToMap())
                     _status.value = Status.DONE
                 } catch (e: Exception) {
                     _status.value = Status.ERROR
@@ -82,12 +101,20 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d
         }
     }
 
-    fun changeGroupStatus(identifier: String, command: Command) {
+    fun changeGroupStatus(identifier: String, group: Group, command: Command) {
         uiScope.launch {
             withContext(Dispatchers.Main) {
                 try {
                     _status.value = Status.LOADING
-                    dataManagerGroups.changeGroupStatus(identifier, command).await()
+                    when (command.action) {
+                        Command.Action.ACTIVATE -> group.status = Group.Status.ACTIVE
+                        Command.Action.REOPEN -> group.status = Group.Status.PENDING
+                        Command.Action.CLOSE -> group.status = Group.Status.CLOSED
+                        Command.Action.LOCK -> group.status = Group.Status.ACTIVE
+                        else -> group.status = Group.Status.PENDING
+                    }
+                    synchronizationManager.updateDocument(identifier, group.serializeToMap())
+                    //   dataManagerGroups.changeGroupStatus(identifier, command).await()
                     _status.value = Status.DONE
                 } catch (e: Exception) {
                     _status.value = Status.ERROR
@@ -102,7 +129,7 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d
 
     fun getCountryCode(countries: List<Country>, countryName: String): String? {
         for (country in countries) {
-            if (country.name.equals(countryName)) {
+            if (country.name == countryName) {
                 return country.alphaCode
             }
         }
@@ -111,7 +138,7 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d
 
     fun isCountryValid(countries: List<Country>, countryName: String): Boolean {
         for (country in countries) {
-            if (country.name.equals(countryName)) {
+            if (country.name == countryName) {
                 return true
             }
         }
@@ -125,6 +152,7 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d
     override fun onCleared() {
         super.onCleared()
         viewModelJob.cancel()
+        synchronizationManager.closeDatabase()
     }
 
 }
\ No newline at end of file
diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModelFactory.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModelFactory.kt
index f541a37..0d713f9 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModelFactory.kt
+++ b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModelFactory.kt
@@ -3,8 +3,9 @@ package org.apache.fineract.ui.online.groups.grouplist
 import android.content.Context
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
+import org.apache.fineract.couchbase.SynchronizationManager
 import org.apache.fineract.data.datamanager.api.DataManagerAnonymous
-import org.apache.fineract.data.datamanager.api.DataManagerGroups
+import org.apache.fineract.data.local.PreferencesHelper
 import org.apache.fineract.injection.ApplicationContext
 import javax.inject.Inject
 
@@ -15,11 +16,13 @@ import javax.inject.Inject
 
 
 class GroupViewModelFactory @Inject constructor(@ApplicationContext var context: Context,
-                                                private val dataManagerGroups: DataManagerGroups,
-                                                private val dataManagerAnonymous: DataManagerAnonymous)
+                                                private val synchronizationManager: SynchronizationManager,
+                                                private val dataManagerAnonymous: DataManagerAnonymous,
+                                                private val preferencesHelper: PreferencesHelper)
     : ViewModelProvider.NewInstanceFactory() {
 
+    @Suppress("UNCHECKED_CAST")
     override fun <T : ViewModel?> create(modelClass: Class<T>): T {
-        return GroupViewModel(dataManagerGroups, dataManagerAnonymous) as T
+        return GroupViewModel(synchronizationManager, dataManagerAnonymous, preferencesHelper) as T
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/grouptasks/GroupTasksBottomSheetFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/grouptasks/GroupTasksBottomSheetFragment.kt
index 76c601d..06893f4 100644
--- a/app/src/main/java/org/apache/fineract/ui/online/groups/grouptasks/GroupTasksBottomSheetFragment.kt
+++ b/app/src/main/java/org/apache/fineract/ui/online/groups/grouptasks/GroupTasksBottomSheetFragment.kt
@@ -123,7 +123,7 @@ class GroupTasksBottomSheetFragment(val group: Group) : FineractBaseBottomSheetD
         command.comment = rootView.et_comment.text.toString().trim { it <= ' ' }
         rootView.et_comment.isEnabled = false
         group.identifier?.let {
-            viewModel.changeGroupStatus(it, command)
+            viewModel.changeGroupStatus(it, group, command)
         }
     }
 
@@ -134,7 +134,7 @@ class GroupTasksBottomSheetFragment(val group: Group) : FineractBaseBottomSheetD
 
     override fun onStart() {
         super.onStart()
-        behavior?.state = BottomSheetBehavior.STATE_EXPANDED
+        behavior.state = BottomSheetBehavior.STATE_EXPANDED
     }
 
     override fun onDestroy() {
diff --git a/app/src/main/java/org/apache/fineract/utils/Constants.kt b/app/src/main/java/org/apache/fineract/utils/Constants.kt
index 6d9dca5..072c90f 100644
--- a/app/src/main/java/org/apache/fineract/utils/Constants.kt
+++ b/app/src/main/java/org/apache/fineract/utils/Constants.kt
@@ -8,4 +8,8 @@ package org.apache.fineract.utils
 object Constants {
     const val GROUP = "group"
     const val GROUP_ACTION = "group_action"
+    const val DATABASE_NAME = "fineract-cn-mobile"
+    const val BASIC_AUTH_KEY = "basic_auth_key"
+    const val GATEWAY_USER_NAME = "fineract-cn"
+    const val GATEWAY_PASSWORD = "password"
 }
\ No newline at end of file
diff --git a/app/src/main/java/org/apache/fineract/utils/DateUtils.java b/app/src/main/java/org/apache/fineract/utils/DateUtils.java
index a582653..d9f8c25 100644
--- a/app/src/main/java/org/apache/fineract/utils/DateUtils.java
+++ b/app/src/main/java/org/apache/fineract/utils/DateUtils.java
@@ -61,7 +61,8 @@ public class DateUtils {
         SimpleDateFormat dateFormat = new SimpleDateFormat(inputFormat, Locale.ENGLISH);
         Date date = new Date();
         try {
-            date = dateFormat.parse(dateString);
+            if (dateString != null)
+                date = dateFormat.parse(dateString);
         } catch (ParseException e) {
             Log.d(LOG_TAG, e.getLocalizedMessage());
         }
diff --git a/app/src/main/java/org/apache/fineract/utils/GsonUtils.kt b/app/src/main/java/org/apache/fineract/utils/GsonUtils.kt
new file mode 100644
index 0000000..449ae0e
--- /dev/null
+++ b/app/src/main/java/org/apache/fineract/utils/GsonUtils.kt
@@ -0,0 +1,31 @@
+package org.apache.fineract.utils
+
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+
+/**
+ * Created by Ahmad Jawid Muhammadi on 7/8/20.
+ */
+
+//convert a data class to a map
+
+fun <T> T.serializeToMap(): Map<String, Any> {
+    return convert()
+}
+
+//convert a map to a data class
+inline fun <reified T> Map<String, Any>.toDataClass(): T {
+    return convert()
+}
+
+//convert an object of type I to type O
+inline fun <I, reified O> I.convert(): O {
+    val json = Gson().toJson(this)
+    return Gson().fromJson(json, object : TypeToken<O>() {}.type)
+}
+
+fun <O> Map<String, Any>.convertToData(type: Class<O>): O {
+    val json = Gson().toJson(this)
+    return Gson().fromJson(json, TypeToken.getParameterized(type).type)
+}
+
diff --git a/app/src/main/resources/groups.json b/app/src/main/resources/groups.json
index 086d27f..85a7b12 100644
--- a/app/src/main/resources/groups.json
+++ b/app/src/main/resources/groups.json
@@ -27,7 +27,8 @@
     "createdOn": "2018-06-26T15:09:36.672Z",
     "createdBy": "ranefer",
     "lastModifiedOn": "lastModifiedOn",
-    "lastModifiedBy": "lastModifiedBy"
+    "lastModifiedBy": "lastModifiedBy",
+    "type": "group"
   },
   {
     "identifier": "group002",
diff --git a/app/src/test/java/org/apache/fineract/online/CustomerPresenterTest.kt b/app/src/test/java/org/apache/fineract/online/CustomerPresenterTest.kt
deleted file mode 100644
index 031a215..0000000
--- a/app/src/test/java/org/apache/fineract/online/CustomerPresenterTest.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-package org.apache.fineract.online
-
-import android.content.Context
-import io.reactivex.Observable
-import org.apache.fineract.FakeRemoteDataSource
-import org.apache.fineract.R
-import org.apache.fineract.data.datamanager.contracts.ManagerCustomer
-import org.apache.fineract.data.datamanager.database.DbManagerCustomer
-import org.apache.fineract.data.models.customer.CustomerPage
-import org.apache.fineract.exceptions.NoConnectivityException
-import org.apache.fineract.ui.online.customers.customerlist.CustomersContract
-import org.apache.fineract.ui.online.customers.customerlist.CustomersPresenter
-import org.apache.fineract.util.RxSchedulersOverrideRule
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnitRunner
-
-@RunWith(MockitoJUnitRunner::class)
-class CustomerPresenterTest {
-
-    @Rule
-    @JvmField
-    val overrideSchedulersRule = RxSchedulersOverrideRule()
-
-    @Mock
-    lateinit var mockedView: CustomersContract.View
-
-    @Mock
-    lateinit var mockedCustomerDataManager: DbManagerCustomer
-
-    lateinit var mockedManagerCustomer: ManagerCustomer
-
-    @Mock
-    lateinit var mockedContext: Context
-
-    lateinit var customerPresenter: CustomersPresenter
-
-    lateinit var customerPage: CustomerPage
-
-    val size = 10
-
-    val pageIndex: Int = 1
-
-    @Before
-    fun setup() {
-        customerPresenter = CustomersPresenter(mockedContext, mockedCustomerDataManager)
-        customerPresenter.attachView(mockedView)
-        customerPage = FakeRemoteDataSource.getCustomerPage()
-        mockedManagerCustomer = mockedCustomerDataManager
-    }
-
-    @Test
-    fun testGetCustomerPage() {
-        `when`(mockedManagerCustomer.fetchCustomers(pageIndex, size))
-                .thenReturn(Observable.just(customerPage))
-        customerPresenter.fetchCustomers(pageIndex, size)
-        verify(mockedView).showProgressbar()
-        verify(mockedView).hideProgressbar()
-        verify(mockedView).showCustomers(customerPage.customers)
-    }
-
-    @Test
-    fun testGetAccountsPageEmpty() {
-        `when`(mockedManagerCustomer.fetchCustomers(pageIndex, size))
-                .thenReturn(Observable.just(CustomerPage(totalPages = 0)))
-        `when`(mockedContext.getString(R.string.empty_customer_list))
-                .thenReturn("Empty customer list")
-        customerPresenter.fetchCustomers(pageIndex, size)
-        verify(mockedView).showProgressbar()
-        verify(mockedView).hideProgressbar()
-        verify(mockedView).showEmptyCustomers("Empty customer list")
-    }
-
-
-    @Test
-    fun testGetAccountsPageFail() {
-        val exception = NoConnectivityException()
-        `when`(mockedCustomerDataManager.fetchCustomers(pageIndex, size))
-                .thenReturn(Observable.error(exception))
-        customerPresenter.fetchCustomers(pageIndex, size)
-        verify(mockedView).showProgressbar()
-        verify(mockedView).hideProgressbar()
-        verify(mockedView).showNoInternetConnection()
-    }
-
-    @After
-    fun tearDown() {
-        customerPresenter.detachView()
-    }
-
-}
\ No newline at end of file
diff --git a/app/sync-gateway-config.json  b/app/sync-gateway-config.json 
new file mode 100644
index 0000000..b3f7592
--- /dev/null
+++ b/app/sync-gateway-config.json 	
@@ -0,0 +1,19 @@
+{
+ "interface":"0.0.0.0:4984",
+  "log": ["*"],
+  "databases": {
+    "fineract-cn": {
+      "server": "ws://localhost:8091/",
+      "bucket": "fineract-cn-mobile",
+      "username": "fineract-cn",
+      "password": "password",
+      "enable_shared_bucket_access": true,
+      "import_docs": true,
+      "num_index_replicas": 0,
+      "users": {
+        "GUEST": { "disabled": false, "admin_channels": ["*"] },
+	    "sync_gateway": {"password": "password"}
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index e663451..b9a7f79 100644
--- a/build.gradle
+++ b/build.gradle
@@ -31,7 +31,7 @@ task clean(type: Delete) {
 
 ext {
     // Sdk and tools
-    minSdkVersion = 16
+    minSdkVersion = 19
     targetSdkVersion = 28
     compileSdkVersion = 28
     buildToolsVersion = '28.0.3'
@@ -61,7 +61,7 @@ ext {
     hamcrestVersion = '1.3'
     runnerVersion = '1.1.0'
     rulesVersion = '1.1.0'
-    espressoVersion = '3.1.0'
+    espressoVersion = '3.2.0'
     sweetErrorVersion = '1.0.0'
     glideVersion = '3.7.0'
     materialStepperVersion = '3.3.0'
@@ -72,4 +72,8 @@ ext {
     mifosPasscodeVersion = '1.0.0'
     easyValidationVersion = '1.0.1'
     version_kotlin_coroutines = '1.3.4'
+    couchbase = '2.7.1'
+    extJunit = "1.1.1"
+    espressoCore = "3.2.0"
+    espressonContribute = "3.2.0"
 }