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:14 UTC

[fineract-cn-mobile] branch development updated (592099e -> b7a5530)

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

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


    from 592099e  :
     new 86c910a  initialize couchbaseLite, implement group & customers  and add UI tests
     new b7a5530  Delete sync-gateway-config.json

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 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 ---------------
 build.gradle                                       |   8 +-
 32 files changed, 798 insertions(+), 412 deletions(-)
 create mode 100644 app/src/androidTest/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivityAndroidTest.kt
 create mode 100644 app/src/androidTest/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragmentAndroidTest.kt
 create mode 100644 app/src/main/java/org/apache/fineract/couchbase/CouchbaseDatabase.kt
 create mode 100644 app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt
 create mode 100644 app/src/main/java/org/apache/fineract/couchbase/Replicate.java
 create mode 100644 app/src/main/java/org/apache/fineract/couchbase/SynchronizationManager.kt
 create mode 100644 app/src/main/java/org/apache/fineract/utils/GsonUtils.kt
 delete mode 100644 app/src/test/java/org/apache/fineract/online/CustomerPresenterTest.kt

[fineract-cn-mobile] 02/02: Delete sync-gateway-config.json

Posted by ra...@apache.org.
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 b7a55308f7e6525c82cf6f74c986dddd321dc4ae
Author: Ahmad Jawid Muhammadi <49...@users.noreply.github.com>
AuthorDate: Sun Sep 13 13:08:06 2020 +0530

    Delete sync-gateway-config.json
---
 app/sync-gateway-config.json  | 19 -------------------
 1 file changed, 19 deletions(-)

diff --git a/app/sync-gateway-config.json  b/app/sync-gateway-config.json 
deleted file mode 100644
index b3f7592..0000000
--- a/app/sync-gateway-config.json 	
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "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

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

Posted by ra...@apache.org.
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"
 }