diff --git a/.travis.yml b/.travis.yml index beb47cd1..219324c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,4 +26,5 @@ android: - extra-google-m2repository - extra-android-m2repository -script: "./gradlew build --stacktrace" \ No newline at end of file +script: "./gradlew build --stacktrace" + "./gradlew build connectedAndroidTest --stacktrace" \ No newline at end of file diff --git a/app/src/androidTest/java/org/apache/fineract/ui/online/accounts/AccountsListFragmentAndroidTest.kt b/app/src/androidTest/java/org/apache/fineract/ui/online/accounts/AccountsListFragmentAndroidTest.kt new file mode 100644 index 00000000..95c33d29 --- /dev/null +++ b/app/src/androidTest/java/org/apache/fineract/ui/online/accounts/AccountsListFragmentAndroidTest.kt @@ -0,0 +1,53 @@ +package org.apache.fineract.ui.online.accounts + +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +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.AccountsAdapter +import org.apache.fineract.ui.online.DashboardActivity +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + + +/** + * Created by Varun Jain on 21/8/2021. + */ + +@RunWith(AndroidJUnit4::class) +class AccountsListFragmentAndroidTest { + + @get: Rule + val activityRule = ActivityTestRule(DashboardActivity::class.java) + + @Test + fun launchAccountListFragmentAndRvItemClickCheck() { + + //Open drawer + Espresso.onView(ViewMatchers.withId(R.id.drawer_layout)) + .perform(DrawerActions.open()) + + // Navigate to Teller list fragment + Espresso.onView(ViewMatchers.withId(R.id.nav_view)) + .perform(NavigationViewActions.navigateTo(R.id.item_accounts)) + + //Click on teller item + Espresso.onView(ViewMatchers.withId(R.id.rvAccount)).perform( + RecyclerViewActions.actionOnItem( + ViewMatchers.hasDescendant(ViewMatchers.withText("account identifier")), + ViewActions.click() + ) + ) + + //Assert if thw item has been displayed correctly + Espresso.onView(ViewMatchers.withId(R.id.tvIdentifierAccount)) + .check(ViewAssertions.matches(ViewMatchers.withText("account identifier"))) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/org/apache/fineract/ui/online/accounts/ChangeAccountStatusAndroidTest.kt b/app/src/androidTest/java/org/apache/fineract/ui/online/accounts/ChangeAccountStatusAndroidTest.kt new file mode 100644 index 00000000..a408f1cb --- /dev/null +++ b/app/src/androidTest/java/org/apache/fineract/ui/online/accounts/ChangeAccountStatusAndroidTest.kt @@ -0,0 +1,102 @@ +package org.apache.fineract.ui.online.accounts + +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +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.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.accounts.Account +import org.apache.fineract.ui.adapters.AccountsAdapter +import org.apache.fineract.ui.online.DashboardActivity +import org.apache.fineract.utils.toDataClass +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Created by Varun Jain on 21/8/2021. + */ + +@RunWith(AndroidJUnit4::class) +class ChangeAccountStatusAndroidTest { + + @get:Rule + val activityRule = ActivityTestRule(DashboardActivity::class.java) + + lateinit var synchronizationManager: SynchronizationManager + + @Before + fun before() { + synchronizationManager = + SynchronizationManager(InstrumentationRegistry.getInstrumentation().context) + } + + @Test + fun testIfAccountStatusChangedSuccessfully() { + + //Open drawer + Espresso.onView(ViewMatchers.withId(R.id.drawer_layout)) + .perform(DrawerActions.open()) + + // Navigate to Account list fragment + Espresso.onView(ViewMatchers.withId(R.id.nav_view)) + .perform(NavigationViewActions.navigateTo(R.id.item_accounts)) + + //Click on recyclerView item + Espresso.onView(ViewMatchers.withId(R.id.rvAccount)).perform( + RecyclerViewActions.actionOnItem( + ViewMatchers.hasDescendant(ViewMatchers.withText("account identifier")), + ViewActions.click() + ) + ) + + //Assert if the item has been displayed correctly + Espresso.onView(ViewMatchers.withId(R.id.tvIdentifierAccount)) + .check(ViewAssertions.matches(ViewMatchers.withText("account identifier"))) + + // Launch the Bottom sheet and click on the change Task Icon + Espresso.onView(ViewMatchers.withId(R.id.llTasksAccountDetails)) + .perform(ViewActions.click()) + + // get current Account model from the synchronization manager + val mapItem = synchronizationManager.getDocumentForTest( + "account identifier", + InstrumentationRegistry.getInstrumentation().context + ) + + val account = mapItem.toDataClass() + val prevStatus = account.state + + // click on the icon to change the account status + Espresso.onView(ViewMatchers.withId(R.id.ivAccountTask)) + .perform(ViewActions.click()) + + Espresso.onView(ViewMatchers.withId(R.id.etCommentAccountTasks)) + .perform(ViewActions.typeText("Changing state of this account")) + .perform(ViewActions.closeSoftKeyboard()) + + // confirm Task change operation + Espresso.onView(ViewMatchers.withId(R.id.btnSubmitAccountTask)) + .perform(ViewActions.click()) + + // assert if the account status was changed successfully + when (prevStatus) { + Account.State.LOCKED -> { + Assert.assertEquals(account.state, Account.State.LOCKED) + } + Account.State.OPEN -> { + Assert.assertEquals(account.state, Account.State.OPEN) + } + } + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/org/apache/fineract/ui/online/accounts/CreateAccountActivityAndroidTest.kt b/app/src/androidTest/java/org/apache/fineract/ui/online/accounts/CreateAccountActivityAndroidTest.kt new file mode 100644 index 00000000..82926b14 --- /dev/null +++ b/app/src/androidTest/java/org/apache/fineract/ui/online/accounts/CreateAccountActivityAndroidTest.kt @@ -0,0 +1,163 @@ +package org.apache.fineract.ui.online.accounts + +import android.content.Intent +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.* +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.matcher.RootMatchers.isDialog +import androidx.test.espresso.matcher.ViewMatchers.* +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.accounts.Account +import org.apache.fineract.data.models.accounts.AccountType +import org.apache.fineract.ui.adapters.NameListAdapter +import org.apache.fineract.ui.online.accounting.accounts.AccountAction +import org.apache.fineract.ui.online.accounting.accounts.createaccount.CreateAccountActivity +import org.apache.fineract.utils.Constants +import org.apache.fineract.utils.toDataClass +import org.junit.* +import org.junit.runner.RunWith + +/** + * Created by Varun Jain on 21/8/2021. + */ + +@RunWith(AndroidJUnit4::class) +class CreateAccountActivityAndroidTest { + + @get:Rule + var activityTestRule = + ActivityTestRule(CreateAccountActivity::class.java, false, false) + + lateinit var synchronizationManager: SynchronizationManager + + @Before + fun before() { + + val intent = Intent().apply { + putExtra(Constants.ACCOUNT_ACTION, AccountAction.CREATE) + } + activityTestRule.launchActivity(intent) + synchronizationManager = + SynchronizationManager(InstrumentationRegistry.getInstrumentation().context) + } + + @Test + fun isCreateAccountStepVisible() { + onView(withId(R.id.slCreateAccount)) + .check(matches(isDisplayed())) + } + + @Test + fun createAccountTest() { + + // Filling all the details in Account Details Step fragment + onView(withId(R.id.etIdentifierAccountDetailsStep)) + .perform(typeText("test account")) + onView(withId(R.id.etNameAccountDetailsStep)) + .perform(typeText("test name")) + onView(withId(R.id.etAltAccountNumAccountDetailsStep)) + .perform(typeText("alternate account number")) + onView(withId(R.id.etLedgerAccountDetailsStep)) + .perform(typeText("test Ledger")) + onView(withId(R.id.scrollViewAccountDetailsStep)) + .perform(swipeUp()) + onView(withId(R.id.etRefAccountNumAccountDetailsStep)) + .perform(typeText("test Reference account number")) + onView(withId(R.id.etBalanceAccountDetailsStep)) + .perform(typeText("1000")) + .perform(closeSoftKeyboard()) + + // Navigate to the next step + onView(withText("NEXT")).perform(click()) + + // Create the Holders + onView(withId(R.id.ivAddHolder)) + .perform(click()) + onView(withId(R.id.etNewHolder)) + .perform(typeText("Account")) + onView(withId(R.id.scrollViewAddAccountHolderStep)) + onView(withId(R.id.btnAddHolder)) + .perform(click()) + + // Edit an Account assignment + onView(withId(R.id.iv_edit)) + .perform(click()) + onView(withId(R.id.etNewHolder)) + .perform(typeText(" test holder")) + onView(withId(R.id.scrollViewAddAccountHolderStep)) + .perform(swipeUp()) + onView(withId(R.id.btnAddHolder)) + .perform(click()) + + // Delete a Account assignment + onView(withId(R.id.iv_delete)) + .perform(click()) + + // Dismiss the Delete dialog + onView(withText("DELETE")).inRoot(isDialog()).check(matches(isDisplayed())).perform(pressBack()); + + // Navigate to the next step + onView(withText("NEXT")).perform(click()) + + // Create the Signature Authorities + onView(withId(R.id.ivAddSignatureAuthorities)) + .perform(click()) + onView(withId(R.id.etNewSignatureAuthorities)) + .perform(typeText("test signature authority")) + onView(withId(R.id.scrollViewAddAccountSignatureAuthorityStep)) + onView(withId(R.id.btnAddSignatureAuthority)) + .perform(click()) + + // Navigate to the next step + onView(withText("NEXT")).perform(click()) + + // Test if the account Review Step is populated with the correct values + onView(withId(R.id.tvIdentifierAccountStepReview)).check(matches(withText("test account"))) + onView(withId(R.id.tvTypeAccountStepReview)).check(matches(withText(AccountType.ASSET.toString()))) + onView(withId(R.id.tvNameAccountStepReview)).check(matches(withText("test name"))) + onView(withId(R.id.scrollViewAccountReviewStep)) + .perform(swipeUp()) + onView(withId(R.id.rvHoldersAccountStepReview)) + .perform( + RecyclerViewActions.actionOnItem( + hasDescendant(withText("Account test holder")), click() + ) + ) + onView(withId(R.id.rvSignatureAuthoritiesAccountStepReview)) + .perform( + RecyclerViewActions.actionOnItem( + hasDescendant(withText("test signature authority")), click() + ) + ) + onView(withId(R.id.tvRefAccountStepReview)).check(matches(withText("test Reference account number"))) + onView(withId(R.id.tvLedgerAccountStepReview)).check(matches(withText("test Ledger"))) + onView(withId(R.id.tvAltAccountNoAccountStepReview)).check(matches(withText("alternate account number"))) + + // Complete creating the account + onView(withText("COMPLETE")).perform(click()) + + // assert if the account was created successfully + val mapItem = synchronizationManager.getDocumentForTest( + "test account", + InstrumentationRegistry.getInstrumentation().context + ) + + val account = mapItem.toDataClass() + + Assert.assertEquals(account.identifier, "test account") + Assert.assertEquals(account.name, "test name") + } + + @After + fun after() { + //delete the created document in test + synchronizationManager.deleteDocument( + "test account" + ) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bad499ef..feb947f2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -90,6 +90,12 @@ + + + + diff --git a/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt b/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt index 9e32fd45..1dae00f1 100644 --- a/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt +++ b/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt @@ -6,5 +6,6 @@ package org.apache.fineract.couchbase enum class DocumentType(val value: String) { GROUP("Group"), - CUSTOMER("customer") + CUSTOMER("customer"), + ACCOUNT("Account") } \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/data/datamanager/DataManagerAccounting.kt b/app/src/main/java/org/apache/fineract/data/datamanager/DataManagerAccounting.kt index ed1304d2..6ce793b2 100644 --- a/app/src/main/java/org/apache/fineract/data/datamanager/DataManagerAccounting.kt +++ b/app/src/main/java/org/apache/fineract/data/datamanager/DataManagerAccounting.kt @@ -1,5 +1,6 @@ package org.apache.fineract.data.datamanager +import io.reactivex.Completable import io.reactivex.Observable import io.reactivex.ObservableSource import io.reactivex.functions.Function @@ -36,6 +37,11 @@ class DataManagerAccounting @Inject constructor(val baseManagerApi: BaseApiManag .onErrorResumeNext(Function> { Observable.just(FakeRemoteDataSource.getAccountPage()) }) + fun createAccount(account: Account) : Completable = + baseManagerApi.accountingService.createAccount(account) + + fun updateAccount(identifier: String, account: Account) : Completable = + baseManagerApi.accountingService.updateAccount(identifier, account) fun findAccount(identifier: String): Observable = baseManagerApi.accountingService.findAccount(identifier) diff --git a/app/src/main/java/org/apache/fineract/data/models/accounts/Account.kt b/app/src/main/java/org/apache/fineract/data/models/accounts/Account.kt index 52761c4a..6d51d3c5 100644 --- a/app/src/main/java/org/apache/fineract/data/models/accounts/Account.kt +++ b/app/src/main/java/org/apache/fineract/data/models/accounts/Account.kt @@ -3,25 +3,26 @@ package org.apache.fineract.data.models.accounts import android.os.Parcelable import com.google.gson.annotations.SerializedName import kotlinx.android.parcel.Parcelize +import org.apache.fineract.couchbase.DocumentType @Parcelize data class Account( - @SerializedName("type") val type: AccountType? = null, - @SerializedName("identifier") val identifier: String? = null, - @SerializedName("name") val name: String? = null, - @SerializedName("holders") val holders: Set? = null, - @SerializedName("signatureAuthorities") val signatureAuthorities: Set? = null, - @SerializedName("balance") val balance: Double? = null, - @SerializedName("referenceAccount") val referenceAccount: String? = null, - @SerializedName("ledger") val ledger: String? = null, - @SerializedName("state") val state: State? = null, - @SerializedName("alternativeAccountNumber") val alternativeAccountNumber: String? = null, - @SerializedName("createdOn") val createdOn: String? = null, - @SerializedName("createdBy") val createdBy: String? = null, - @SerializedName("lastModifiedOn") val lastModifiedOn: String? = null, - @SerializedName("lastModifiedBy") val lastModifiedBy: String? = null - + var type: AccountType? = null, + var identifier: String? = null, + var name: String? = null, + var holders: List? = null, + var signatureAuthorities: List? = null, + var balance: Double? = null, + var referenceAccount: String? = null, + var ledger: String? = null, + var state: State? = null, + var alternativeAccountNumber: String? = null, + var createdOn: String? = null, + var createdBy: String? = null, + var lastModifiedOn: String? = null, + var lastModifiedBy: String? = null, + var documentType: String = DocumentType.ACCOUNT.value ) : Parcelable { enum class State { diff --git a/app/src/main/java/org/apache/fineract/data/services/AccountingService.kt b/app/src/main/java/org/apache/fineract/data/services/AccountingService.kt index 491e11e1..fb52ba44 100644 --- a/app/src/main/java/org/apache/fineract/data/services/AccountingService.kt +++ b/app/src/main/java/org/apache/fineract/data/services/AccountingService.kt @@ -1,13 +1,13 @@ package org.apache.fineract.data.services +import io.reactivex.Completable import io.reactivex.Observable import org.apache.fineract.data.models.accounts.Account import org.apache.fineract.data.models.accounts.AccountPage import org.apache.fineract.data.models.accounts.Ledger import org.apache.fineract.data.models.accounts.LedgerPage import org.apache.fineract.data.remote.EndPoints -import retrofit2.http.GET -import retrofit2.http.Path +import retrofit2.http.* interface AccountingService { @@ -24,4 +24,11 @@ interface AccountingService { @GET(EndPoints.API_ACCOUNTING_PATH + "/accounts/{identifier}") fun findAccount(@Path("identifier") identifier: String): Observable + @POST(EndPoints.API_ACCOUNTING_PATH + "/accounts") + fun createAccount(@Body account: Account) : Completable + + @PUT(EndPoints.API_ACCOUNTING_PATH + "/accounts/{identifier}") + fun updateAccount(@Path("identifier") identifier: String, + @Body account: Account) : Completable + } \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/injection/component/ActivityComponent.java b/app/src/main/java/org/apache/fineract/injection/component/ActivityComponent.java index 7e8d2ec0..1a4fb00a 100644 --- a/app/src/main/java/org/apache/fineract/injection/component/ActivityComponent.java +++ b/app/src/main/java/org/apache/fineract/injection/component/ActivityComponent.java @@ -4,7 +4,15 @@ import org.apache.fineract.injection.module.ActivityModule; import org.apache.fineract.ui.offline.CustomerPayloadFragment; import org.apache.fineract.ui.online.DashboardActivity; +import org.apache.fineract.ui.online.accounting.accounts.accountDetails.AccountDetailsActivity; +import org.apache.fineract.ui.online.accounting.accounts.accountDetails.AccountDetailsFragment; import org.apache.fineract.ui.online.accounting.accounts.AccountsFragment; +import org.apache.fineract.ui.online.accounting.accounts.accounttasks.AccountTaskBottomSheetFragment; +import org.apache.fineract.ui.online.accounting.accounts.createaccount.AccountDetailsStepFragment; +import org.apache.fineract.ui.online.accounting.accounts.createaccount.AccountReviewStepFragment; +import org.apache.fineract.ui.online.accounting.accounts.createaccount.AddAccountHoldersStepFragment; +import org.apache.fineract.ui.online.accounting.accounts.createaccount.AddAccountSignAuthoritiesFragment; +import org.apache.fineract.ui.online.accounting.accounts.createaccount.CreateAccountActivity; import org.apache.fineract.ui.online.accounting.ledgers.LedgerFragment; import org.apache.fineract.ui.online.customers.createcustomer.customeractivity .CreateCustomerActivity; @@ -159,5 +167,21 @@ public interface ActivityComponent { void inject(GroupDetailsFragment groupDetailsFragment); void inject(GroupTasksBottomSheetFragment groupTasksBottomSheetFragment); + + void inject(AccountDetailsActivity accountDetailsActivity); + + void inject(AccountDetailsFragment accountDetailsFragment); + + void inject(CreateAccountActivity createAccountActivity); + + void inject(AccountDetailsStepFragment accountDetailsStepFragment); + + void inject(AccountReviewStepFragment accountReviewStepFragment); + + void inject(AddAccountSignAuthoritiesFragment addAccountSignAuthoritiesFragment); + + void inject(AddAccountHoldersStepFragment addAccountHoldersStepFragment); + + void inject(AccountTaskBottomSheetFragment accountTaskBottomSheetFragment); } diff --git a/app/src/main/java/org/apache/fineract/ui/adapters/AccountsAdapter.kt b/app/src/main/java/org/apache/fineract/ui/adapters/AccountsAdapter.kt index 6205ad7f..7b6d97d9 100644 --- a/app/src/main/java/org/apache/fineract/ui/adapters/AccountsAdapter.kt +++ b/app/src/main/java/org/apache/fineract/ui/adapters/AccountsAdapter.kt @@ -6,11 +6,13 @@ import androidx.recyclerview.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout import android.widget.TextView import kotlinx.android.synthetic.main.item_account.view.* import org.apache.fineract.R import org.apache.fineract.data.models.accounts.Account import org.apache.fineract.injection.ApplicationContext +import org.apache.fineract.ui.base.OnItemClickListener import org.apache.fineract.utils.DateUtils import org.apache.fineract.utils.StatusUtils import javax.inject.Inject @@ -20,6 +22,8 @@ class AccountsAdapter @Inject constructor(@ApplicationContext val context: Conte var accounts: List = ArrayList() + lateinit var onItemClickListener: OnItemClickListener + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_account, parent, false) @@ -55,12 +59,27 @@ class AccountsAdapter @Inject constructor(@ApplicationContext val context: Conte notifyDataSetChanged() } - class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + fun setItemClickListener(onItemClickListener: OnItemClickListener) { + this.onItemClickListener = onItemClickListener + } + + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { val tvIdentifier: TextView = itemView.tv_account_identifier val tvModifiedBy: TextView = itemView.tv_modified_by val tvModifiedOn: TextView = itemView.tv_modified_on val tvName: TextView = itemView.tv_name val ivAccountTypeIndicator: AppCompatImageView = itemView.iv_type_indicator + val llAccount: LinearLayout = itemView.ll_account + + init { + llAccount.setOnClickListener(this) + } + + override fun onClick(v: View?) { + if (onItemClickListener != null) { + onItemClickListener.onItemClick(v, adapterPosition) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/AccountAction.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/AccountAction.kt new file mode 100644 index 00000000..e9c212e7 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/AccountAction.kt @@ -0,0 +1,10 @@ +package org.apache.fineract.ui.online.accounting.accounts + +/** + * Created by Varun Jain on 23/July/2021 + */ + +enum class AccountAction { + EDIT, + CREATE +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/AccountsFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/AccountsFragment.kt index 67c27d8a..1b15eeed 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/AccountsFragment.kt +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/AccountsFragment.kt @@ -2,34 +2,53 @@ package org.apache.fineract.ui.online.accounting.accounts import android.app.SearchManager import android.content.Context +import android.content.Intent import android.os.Bundle import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.recyclerview.widget.LinearLayoutManager import androidx.appcompat.widget.SearchView import android.text.TextUtils import android.view.* +import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.RecyclerView +import butterknife.ButterKnife import kotlinx.android.synthetic.main.fragment_accounts.* import kotlinx.android.synthetic.main.layout_exception_handler.* import org.apache.fineract.R +import androidx.lifecycle.Observer +import butterknife.OnClick import org.apache.fineract.data.models.accounts.Account import org.apache.fineract.ui.adapters.AccountsAdapter import org.apache.fineract.ui.base.FineractBaseActivity import org.apache.fineract.ui.base.FineractBaseFragment -import java.util.* +import org.apache.fineract.ui.base.OnItemClickListener +import org.apache.fineract.ui.online.accounting.accounts.accountDetails.AccountDetailsActivity +import org.apache.fineract.ui.online.accounting.accounts.createaccount.CreateAccountActivity +import org.apache.fineract.ui.online.accounting.accounts.viewmodel.AccountsViewModel +import org.apache.fineract.ui.online.accounting.accounts.viewmodel.AccountsViewModelFactory +import org.apache.fineract.utils.Constants import javax.inject.Inject +import kotlin.collections.ArrayList -class AccountsFragment : FineractBaseFragment(), AccountContract.View, SwipeRefreshLayout.OnRefreshListener { +class AccountsFragment : FineractBaseFragment(), OnItemClickListener, SwipeRefreshLayout.OnRefreshListener { + + lateinit var rootView: View + + lateinit var accountsViewModel: AccountsViewModel @Inject - lateinit var accountsPresenter: AccountsPresenter + lateinit var accountsViewModelFactory: AccountsViewModelFactory @Inject lateinit var accountsAdapter: AccountsAdapter lateinit var accountList : List + val searchedAccount: (ArrayList) -> Unit = { accounts -> + accountsAdapter.setAccountsList(accounts) + } + companion object { fun newInstance() = AccountsFragment() } @@ -45,25 +64,36 @@ class AccountsFragment : FineractBaseFragment(), AccountContract.View, SwipeRefr val rootView = inflater.inflate(R.layout.fragment_accounts, container, false) (activity as FineractBaseActivity).activityComponent.inject(this) - accountsPresenter.attachView(this) - initializeFineractUIErrorHandler(activity, rootView) - + accountsViewModel = ViewModelProviders.of(this, accountsViewModelFactory).get(AccountsViewModel::class.java) + ButterKnife.bind(this, rootView) return rootView } + + override fun onStart() { + super.onStart() + accountsViewModel.getAccounts()?.observe(this, Observer { + it?.let { + accountList = it + accountsAdapter.setAccountsList(it) + } + }) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + accountsAdapter.setItemClickListener(this) showUserInterface() btn_try_again.setOnClickListener { + showProgressbar() layoutError.visibility = View.GONE - accountsPresenter.getAccountsPage() + accountsViewModel.getAccounts() + hideProgressbar() } - - accountsPresenter.getAccountsPage() } - override fun showUserInterface() { + fun showUserInterface() { setToolbarTitle(getString(R.string.accounts)) val layoutManager = LinearLayoutManager(activity) layoutManager.orientation = RecyclerView.VERTICAL @@ -93,7 +123,7 @@ class AccountsFragment : FineractBaseFragment(), AccountContract.View, SwipeRefr searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { - accountsPresenter.searchAccount(accountList, query) + accountsViewModel.searchAccount(accountList as ArrayList, query, searchedAccount) return false } @@ -102,36 +132,43 @@ class AccountsFragment : FineractBaseFragment(), AccountContract.View, SwipeRefr showRecyclerView(true) accountsAdapter.setAccountsList(accountList) } - accountsPresenter.searchAccount(accountList, newText) + accountsViewModel.searchAccount(accountList as ArrayList, newText, searchedAccount) return false } }) } - override fun searchedAccount(accounts: List) { + fun searchedAccount(accounts: List) { showRecyclerView(true) accountsAdapter.setAccountsList(accounts) } override fun onRefresh() { - accountsPresenter.getAccountsPage() + showProgressbar() + accountsViewModel.getAccounts()?.observe(this, Observer { + it?.let { + accountList = it + accountsAdapter.setAccountsList(it) + } + }) + hideProgressbar() } - override fun showAccounts(accounts: List) { + fun showAccounts(accounts: List) { showRecyclerView(true) accountList = accounts accountsAdapter.setAccountsList(accountList) } - override fun showEmptyAccounts() { + fun showEmptyAccounts() { showRecyclerView(false) - showFineractEmptyUI(getString(R.string.accounts), getString(R.string.accounts), - R.drawable.ic_person_outline_black_24dp) + showFineractEmptyUI(getString(R.string.account), getString(R.string.account), + R.drawable.ic_person_outline_black_24dp) } - override fun showRecyclerView(status: Boolean) { + fun showRecyclerView(status: Boolean) { if (status) { rvAccount.visibility = View.VISIBLE layoutError.visibility = View.GONE @@ -141,27 +178,38 @@ class AccountsFragment : FineractBaseFragment(), AccountContract.View, SwipeRefr } } - override fun showProgressbar() { + fun showProgressbar() { swipeContainer.isRefreshing = true } - override fun hideProgressbar() { + fun hideProgressbar() { swipeContainer.isRefreshing = false } - override fun showNoInternetConnection() { + fun showNoInternetConnection() { showRecyclerView(false) showFineractNoInternetUI() } - override fun showError(message: String) { + fun showError(message: String) { showRecyclerView(false) showFineractErrorUI(getString(R.string.accounts)) } - override fun onDestroyView() { - super.onDestroyView() - accountsPresenter.detachView() + override fun onItemClick(childView: View?, position: Int) { + val intent = Intent(context, AccountDetailsActivity::class.java).apply { + putExtra(Constants.ACCOUNT, accountList[position]) + } + startActivity(intent) } + override fun onItemLongPress(childView: View?, position: Int) {} + + @OnClick(R.id.fabAddAccount) + fun addAccount() { + val intent = Intent(activity, CreateAccountActivity::class.java).apply { + putExtra(Constants.ACCOUNT_ACTION, AccountAction.CREATE) + } + startActivity(intent) + } } diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accountDetails/AccountDetailsActivity.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accountDetails/AccountDetailsActivity.kt new file mode 100644 index 00000000..d17e8331 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accountDetails/AccountDetailsActivity.kt @@ -0,0 +1,21 @@ +package org.apache.fineract.ui.online.accounting.accounts.accountDetails + +import android.os.Bundle +import org.apache.fineract.R +import org.apache.fineract.ui.base.FineractBaseActivity +import org.apache.fineract.utils.Constants + +/** + * Created by Varun Jain on 17/July/2021 + */ + +class AccountDetailsActivity : FineractBaseActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_toolbar_container) + + replaceFragment(AccountDetailsFragment.newInstance(intent.getParcelableExtra(Constants.ACCOUNT)), false, R.id.container) + showBackButton() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accountDetails/AccountDetailsFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accountDetails/AccountDetailsFragment.kt new file mode 100644 index 00000000..9bb25993 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accountDetails/AccountDetailsFragment.kt @@ -0,0 +1,155 @@ +package org.apache.fineract.ui.online.accounting.accounts.accountDetails + +import android.content.DialogInterface +import android.content.Intent +import android.os.Bundle +import android.view.* +import androidx.lifecycle.ViewModelProviders +import butterknife.ButterKnife +import butterknife.OnClick +import kotlinx.android.synthetic.main.fragment_account_details.* +import kotlinx.android.synthetic.main.fragment_account_details.view.* +import org.apache.fineract.R +import org.apache.fineract.data.models.accounts.Account +import org.apache.fineract.ui.adapters.NameListAdapter +import org.apache.fineract.ui.base.FineractBaseActivity +import org.apache.fineract.ui.base.FineractBaseFragment +import org.apache.fineract.ui.online.accounting.accounts.AccountAction +import org.apache.fineract.ui.online.accounting.accounts.accounttasks.AccountTaskBottomSheetFragment +import org.apache.fineract.ui.online.accounting.accounts.createaccount.CreateAccountActivity +import org.apache.fineract.ui.online.accounting.accounts.viewmodel.AccountsViewModel +import org.apache.fineract.ui.online.accounting.accounts.viewmodel.AccountsViewModelFactory +import org.apache.fineract.ui.views.CircularImageView +import org.apache.fineract.utils.Constants +import org.apache.fineract.utils.MaterialDialog +import org.apache.fineract.utils.Utils +import javax.inject.Inject + + +/* + * Created by Varun Jain on 17/July/2021 +*/ + +class AccountDetailsFragment : FineractBaseFragment() { + + lateinit var rootView: View + lateinit var account: Account + + @Inject + lateinit var holdersNameAdapter: NameListAdapter + + @Inject + lateinit var signatureAuthoritiesNameAdapter: NameListAdapter + + private lateinit var viewModel: AccountsViewModel + + @Inject + lateinit var viewModelFactory: AccountsViewModelFactory + + companion object { + fun newInstance(account: Account) : AccountDetailsFragment{ + val fragment = AccountDetailsFragment() + val args = Bundle() + args.putParcelable(Constants.ACCOUNT, account) + fragment.arguments = args + return fragment + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + account = arguments?.get(Constants.ACCOUNT) as Account + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + rootView = inflater.inflate(R.layout.fragment_account_details, container, false) + (activity as FineractBaseActivity).activityComponent.inject(this) + viewModel = ViewModelProviders.of(this, viewModelFactory).get(AccountsViewModel::class.java) + ButterKnife.bind(this, rootView) + rootView.rvHoldersAccount.adapter = holdersNameAdapter + holdersNameAdapter.setReview(true) + rootView.rvSignatureAuthoritiesAccount.adapter = signatureAuthoritiesNameAdapter + signatureAuthoritiesNameAdapter.setReview(true) + setToolbarTitle(account.identifier) + setHasOptionsMenu(true) + return rootView + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + tvIdentifierAccount.text = account.identifier + tvAccountType.text = account.type.toString() + setAccountStatusIcon(account.state, civStateAccount) + account.holders?.let{ + holdersNameAdapter.submitList(it as ArrayList) + } + account.signatureAuthorities?.let { + signatureAuthoritiesNameAdapter.submitList(it as ArrayList) + } + tvStateAccount.text = account.state.toString() + tvNameAccount.text = account.name + tvBalanceAccount.text = account.balance.toString() + tvReferenceAmountAccount.text = account.referenceAccount + tvLedgersAccount.text = account.ledger + tvAltAccountNo.text = account.alternativeAccountNumber + } + + override fun onPrepareOptionsMenu(menu: Menu) { + super.onPrepareOptionsMenu(menu) + Utils.setToolbarIconColor(context, menu, R.color.white) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.menu_account_details, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.menuEditAccount -> { + val intent = Intent(activity, CreateAccountActivity::class.java).apply { + putExtra(Constants.ACCOUNT, account) + putExtra(Constants.ACCOUNT_ACTION, AccountAction.EDIT) + } + startActivity(intent) + activity!!.finish() + } + R.id.menuDeleteAccount -> { + MaterialDialog.Builder().init(context).apply { + setTitle(getString(R.string.dialog_title_confirm_deletion)) + setMessage(getString(R.string.dialog_message_confirm_name_deletion, account.name)) + setPositiveButton(getString(R.string.delete) + ) { dialog: DialogInterface?, _ -> + viewModel.deleteAccount(account) + dialog?.dismiss() + activity!!.finish() + } + setNegativeButton(getString(R.string.dialog_action_cancel)) + createMaterialDialog() + }.run { show() } + } + } + return super.onOptionsItemSelected(item) + } + + @OnClick(R.id.cvTasksAccount) + fun onTasksCardViewClicked() { + val bottomSheet = AccountTaskBottomSheetFragment(account) + bottomSheet.show(childFragmentManager, getString(R.string.tasks)) + } + + private fun setAccountStatusIcon(status: Account.State?, imageView: CircularImageView) { + when (status) { + Account.State.OPEN -> { + imageView.setImageDrawable(Utils.setCircularBackground(R.color.blue, context)) + } + Account.State.CLOSED -> { + imageView.setImageDrawable(Utils.setCircularBackground(R.color.red_dark, context)) + } + Account.State.LOCKED -> { + imageView.setImageDrawable(Utils.setCircularBackground(R.color.light_yellow, context)) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accounttasks/AccountCommand.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accounttasks/AccountCommand.kt new file mode 100644 index 00000000..f15a1bd4 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accounttasks/AccountCommand.kt @@ -0,0 +1,27 @@ +package org.apache.fineract.ui.online.accounting.accounts.accounttasks + +import com.google.gson.annotations.SerializedName + + +/** + * Created by Varun Jain on 23/July/2021 + */ + +data class AccountCommand( + @SerializedName("action") var action: AccountTaskAction? = null, + @SerializedName("comment") var comment: String? = null +) { + enum class AccountTaskAction { + @SerializedName("LOCK") + LOCK, + + @SerializedName("UNLOCK") + UNLOCK, + + @SerializedName("REOPEN") + REOPEN, + + @SerializedName("CLOSE") + CLOSE, + } +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accounttasks/AccountTaskBottomSheetFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accounttasks/AccountTaskBottomSheetFragment.kt new file mode 100644 index 00000000..3fdce78a --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/accounttasks/AccountTaskBottomSheetFragment.kt @@ -0,0 +1,171 @@ +package org.apache.fineract.ui.online.accounting.accounts.accounttasks + +import android.app.Dialog +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.core.content.ContextCompat +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import butterknife.ButterKnife +import butterknife.OnClick +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import kotlinx.android.synthetic.main.fragment_account_tasks_bottom_sheet.view.* +import kotlinx.android.synthetic.main.fragment_group_tasks_bottom_sheet.view.* +import org.apache.fineract.R +import org.apache.fineract.data.Status +import org.apache.fineract.data.models.accounts.Account +import org.apache.fineract.ui.base.FineractBaseActivity +import org.apache.fineract.ui.base.FineractBaseBottomSheetDialogFragment +import org.apache.fineract.ui.base.Toaster +import org.apache.fineract.ui.online.accounting.accounts.viewmodel.AccountsViewModel +import org.apache.fineract.ui.online.accounting.accounts.viewmodel.AccountsViewModelFactory +import javax.inject.Inject + + +/** + * Created by Varun Jain on 24/July/2021 + */ + +class AccountTaskBottomSheetFragment(val account: Account) : FineractBaseBottomSheetDialogFragment() { + + lateinit var rootView: View + private var accountCommand = AccountCommand() + lateinit var behavior: BottomSheetBehavior<*> + private lateinit var viewModel: AccountsViewModel + + @Inject + lateinit var viewModelFactory: AccountsViewModelFactory + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog + rootView = View.inflate(context, R.layout.fragment_account_tasks_bottom_sheet, null) + dialog.setContentView(rootView) + behavior = BottomSheetBehavior.from(rootView.parent as View) + (activity as FineractBaseActivity).activityComponent.inject(this) + ButterKnife.bind(this, rootView) + viewModel = ViewModelProviders.of(this, viewModelFactory).get(AccountsViewModel::class.java) + setDataOnViews() + + subscribeUI() + return dialog + } + + private fun subscribeUI() { + viewModel.status.observe(this, Observer { status -> + when (status) { + Status.LOADING -> { + showMifosProgressDialog(getString(R.string.please_wait_updating_account_status)) + } + Status.ERROR -> { + hideMifosProgressDialog() + Toaster.show(rootView, R.string.error_while_updating_account_status, Toast.LENGTH_SHORT) + } + Status.DONE -> { + Toaster.show(rootView, getString(R.string.account_identifier_updated_successfully, account.identifier), Toast.LENGTH_SHORT) + hideMifosProgressDialog() + dismiss() + } + } + }) + } + + private fun setDataOnViews() { + when (account.state) { + Account.State.OPEN -> { + rootView.ivAccountTask.setImageDrawable( + ContextCompat.getDrawable(activity!!, R.drawable.ic_lock_black_24dp)) + rootView.ivAccountTask.setColorFilter(ContextCompat.getColor(activity!!, R.color.red_dark)) + rootView.tvAccountTask.text = getString(R.string.lock) + rootView.llAccountTask2.visibility = View.VISIBLE + rootView.ivAccountTask2.setImageDrawable( + ContextCompat.getDrawable(activity!!, R.drawable.ic_close_black_24dp)) + rootView.ivAccountTask2.setColorFilter(ContextCompat.getColor(activity!!, R.color.red_dark)) + rootView.tvAccountTask2.text = getString(R.string.close) + + } + Account.State.LOCKED -> { + rootView.ivAccountTask.setImageDrawable( + ContextCompat.getDrawable(activity!!, R.drawable.ic_lock_open_black_24dp)) + rootView.ivAccountTask.setColorFilter(ContextCompat.getColor(activity!!, R.color.status)) + rootView.tvAccountTask.text = getString(R.string.un_lock) + + } + Account.State.CLOSED -> { + rootView.ivAccountTask.setImageDrawable( + ContextCompat.getDrawable(activity!!, R.drawable.ic_check_circle_black_24dp)) + rootView.ivAccountTask.setColorFilter(ContextCompat.getColor(activity!!, R.color.status)) + rootView.tvAccountTask.text = getString(R.string.reopen) + + } + } + } + + @OnClick(R.id.ivAccountTask) + fun onTaskImageViewClicked() { + when (account.state) { + Account.State.OPEN -> { + accountCommand.action = AccountCommand.AccountTaskAction.LOCK + rootView.tvAccountTaskHeader.text = getString(R.string.lock) + rootView.tvAccountTaskSubHeader.text = getString(R.string.please_verify_following_account_task, getString(R.string.lock)) + rootView.btnSubmitAccountTask.text = getString(R.string.lock) + } + Account.State.LOCKED -> { + accountCommand.action = AccountCommand.AccountTaskAction.UNLOCK + rootView.tvAccountTaskHeader.text = getString(R.string.un_lock) + rootView.tvAccountTaskSubHeader.text = getString(R.string.please_verify_following_account_task, getString(R.string.un_lock)) + rootView.btnSubmitAccountTask.text = getString(R.string.un_lock) + } + Account.State.CLOSED -> { + accountCommand.action = AccountCommand.AccountTaskAction.REOPEN + rootView.tvAccountTaskHeader.text = getString(R.string.reopen) + rootView.tvAccountTaskSubHeader.text = getString(R.string.please_verify_following_account_task, getString(R.string.reopen)) + rootView.btnSubmitAccountTask.text = getString(R.string.reopen) + } + } + rootView.llAccountTaskList.visibility = View.GONE + rootView.llAccountTaskForm.visibility = View.VISIBLE + } + + @OnClick(R.id.ivAccountTask2) + fun onTaskImageView2Clicked() { + when (account.state) { + Account.State.OPEN -> { + accountCommand.action = AccountCommand.AccountTaskAction.CLOSE + rootView.tvAccountTaskHeader.text = getString(R.string.close) + rootView.tvAccountTaskSubHeader.text = getString(R.string.please_verify_following_account_task, getString(R.string.close)) + rootView.btnSubmitAccountTask.text = getString(R.string.close) + } + } + rootView.llAccountTask2.visibility = View.INVISIBLE + rootView.llAccountTaskList.visibility = View.GONE + rootView.llAccountTaskForm.visibility = View.VISIBLE + } + + @OnClick(R.id.btnSubmitAccountTask) + fun submitTask() { + accountCommand.comment = rootView.etCommentAccountTasks.text.toString().trim { it <= ' ' } + rootView.etCommentAccountTasks.isEnabled = false + account.identifier?.let { + viewModel.changeAccountStatus(it, account, accountCommand) + } + activity!!.finish() + } + + @OnClick(R.id.btnCancelAccountTasks) + fun onCancel() { + dismiss() + } + + override fun onStart() { + super.onStart() + behavior.state = BottomSheetBehavior.STATE_EXPANDED + } + + override fun onDestroy() { + super.onDestroy() + hideMifosProgressBar() + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AccountDetailsStepFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AccountDetailsStepFragment.kt new file mode 100644 index 00000000..3897e1f9 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AccountDetailsStepFragment.kt @@ -0,0 +1,166 @@ +package org.apache.fineract.ui.online.accounting.accounts.createaccount + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import com.stepstone.stepper.Step +import com.stepstone.stepper.VerificationError +import com.wajahatkarim3.easyvalidation.core.view_ktx.validator +import kotlinx.android.synthetic.main.fragment_step_account_details.* +import kotlinx.android.synthetic.main.fragment_step_account_details.view.* +import kotlinx.android.synthetic.main.fragment_step_group_details.* +import kotlinx.android.synthetic.main.fragment_step_group_details.view.* +import org.apache.fineract.R +import org.apache.fineract.data.models.accounts.AccountType +import org.apache.fineract.ui.base.FineractBaseFragment +import org.apache.fineract.ui.base.OnItemClickListener +import org.apache.fineract.ui.online.accounting.accounts.AccountAction +import org.apache.fineract.utils.Constants + + +/* + * Created by Varun Jain on 21/July/2021 +*/ + +class AccountDetailsStepFragment : FineractBaseFragment(), Step { + + + lateinit var rootView: View + private lateinit var accountAction: AccountAction + + companion object { + fun newInstance(accountAction: AccountAction) = + AccountDetailsStepFragment().apply { + val bundle = Bundle().apply { + putSerializable(Constants.ACCOUNT_ACTION, accountAction) + } + arguments = bundle + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.getSerializable(Constants.ACCOUNT_ACTION)?.let { + accountAction = it as AccountAction + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + rootView = inflater.inflate(R.layout.fragment_step_account_details, container, false) + if (accountAction == AccountAction.EDIT) { + showDataOnViews() + } + + return rootView + } + + private fun showDataOnViews() { + val account = (activity as CreateAccountActivity).getAccount() + rootView.etIdentifierAccountDetailsStep.setText(account.identifier) + rootView.etIdentifierAccountDetailsStep.isEnabled = false + rootView.spinnerTypeAccountDetailsStep.setSelection(getIndexFromAccountType(account.type)) + rootView.etNameAccountDetailsStep.setText(account.name) + rootView.etAltAccountNumAccountDetailsStep.setText(account.alternativeAccountNumber) + rootView.etLedgerAccountDetailsStep.setText(account.ledger) + rootView.etRefAccountNumAccountDetailsStep.setText(account.referenceAccount) + rootView.etBalanceAccountDetailsStep.setText(account.balance.toString()) + } + + private fun getIndexFromAccountType(accountType: AccountType?): Int { + when (accountType) { + AccountType.ASSET -> return 0 + AccountType.LIABILITY -> return 1 + AccountType.EQUITY -> return 2 + AccountType.REVENUE -> return 3 + AccountType.EXPENSE -> return 4 + else -> return 0 + } + } + + private fun getAccountTypeFromIndex(index: Int): AccountType { + when (index) { + 0 -> return AccountType.ASSET + 1 -> return AccountType.LIABILITY + 2 -> return AccountType.EQUITY + 3 -> return AccountType.REVENUE + 4 -> return AccountType.EXPENSE + else -> return AccountType.ASSET + } + } + + override fun verifyStep(): VerificationError? { + if (!validateAlternateAccNo() || !validateBalance() || !validateName() + || !validateLedger() || !validateRefAccountNum() + ) { + return VerificationError(null) + } + (activity as CreateAccountActivity).setAccountDetails( + etIdentifierAccountDetailsStep.text.toString(), + etBalanceAccountDetailsStep.text.toString().toDouble(), + if (getAccountTypeFromIndex(spinnerTypeAccountDetailsStep.selectedItemPosition) != null) getAccountTypeFromIndex( + spinnerTypeAccountDetailsStep.selectedItemPosition + ) else AccountType.ASSET, + etAltAccountNumAccountDetailsStep.text.toString(), + etNameAccountDetailsStep.text.toString(), + etLedgerAccountDetailsStep.text.toString(), + etRefAccountNumAccountDetailsStep.text.toString() + ) + return null + } + + override fun onSelected() {} + + override fun onError(p0: VerificationError) {} + + private fun validateLedger(): Boolean { + return etLedgerAccountDetailsStep.validator() + .minLength(5) + .addErrorCallback { + etLedgerAccountDetailsStep.error = it + } + .check() + } + + private fun validateName(): Boolean { + return etNameAccountDetailsStep.validator() + .nonEmpty() + .addErrorCallback { + etNameAccountDetailsStep.error = it + } + .check() + } + + private fun validateBalance(): Boolean { + return etBalanceAccountDetailsStep.validator() + .addErrorCallback { + etBalanceAccountDetailsStep.error = it + } + .nonEmpty() + .check() + } + + private fun validateRefAccountNum(): Boolean { + return etRefAccountNumAccountDetailsStep.validator() + .minLength(5) + .addErrorCallback { + etRefAccountNumAccountDetailsStep.error = it + } + .check() + } + + private fun validateAlternateAccNo(): Boolean { + return etAltAccountNumAccountDetailsStep.validator() + .minLength(5) + .addErrorCallback { + etAltAccountNumAccountDetailsStep.error = it + } + .check() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AccountReviewStepFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AccountReviewStepFragment.kt new file mode 100644 index 00000000..ff1ef0c8 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AccountReviewStepFragment.kt @@ -0,0 +1,80 @@ +package org.apache.fineract.ui.online.accounting.accounts.createaccount + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.stepstone.stepper.Step +import com.stepstone.stepper.VerificationError +import kotlinx.android.synthetic.main.fragment_account_details.view.* +import kotlinx.android.synthetic.main.fragment_step_account_review.* +import kotlinx.android.synthetic.main.fragment_step_account_review.view.* +import org.apache.fineract.R +import org.apache.fineract.data.models.accounts.Account +import org.apache.fineract.ui.adapters.NameListAdapter +import org.apache.fineract.ui.base.FineractBaseActivity +import org.apache.fineract.ui.base.FineractBaseFragment +import javax.inject.Inject + + +/* + * Created by Varun Jain on 21/July/2021 +*/ + +class AccountReviewStepFragment : FineractBaseFragment(), Step { + + lateinit var rootView: View + + @Inject + lateinit var holdersAdapter: NameListAdapter + + @Inject + lateinit var signatureAuthoritiesAdapter: NameListAdapter + + companion object { + fun newInstance(): AccountReviewStepFragment { + return AccountReviewStepFragment() + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + rootView = inflater.inflate(R.layout.fragment_step_account_review, container, false) + (activity as FineractBaseActivity).activityComponent.inject(this) + rootView.rvSignatureAuthoritiesAccountStepReview.adapter = signatureAuthoritiesAdapter + signatureAuthoritiesAdapter.setReview(true) + rootView.rvHoldersAccountStepReview.adapter = holdersAdapter + holdersAdapter.setReview(true) + + return rootView + } + + override fun verifyStep(): VerificationError? { + return null + } + + private fun populateView(account : Account) { + tvIdentifierAccountStepReview.text = account.identifier + tvTypeAccountStepReview.text = account.type.toString() + tvNameAccountStepReview.text = account.name + account.holders?.let { + holdersAdapter.submitList(it as ArrayList) + } + account.signatureAuthorities?.let { + signatureAuthoritiesAdapter.submitList(it as ArrayList) + } + tvBalanceAccountStepReview.text = account.balance.toString() + tvRefAccountStepReview.text = account.referenceAccount + tvLedgerAccountStepReview.text = account.ledger + tvAltAccountNoAccountStepReview.text = account.alternativeAccountNumber + } + + override fun onSelected() { + populateView((activity as CreateAccountActivity).getAccount()) + } + + override fun onError(p0: VerificationError) {} +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AddAccountHoldersStepFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AddAccountHoldersStepFragment.kt new file mode 100644 index 00000000..d622af35 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AddAccountHoldersStepFragment.kt @@ -0,0 +1,180 @@ +package org.apache.fineract.ui.online.accounting.accounts.createaccount + +import android.content.Context +import android.content.DialogInterface +import android.os.Bundle +import android.util.AttributeSet +import android.view.LayoutInflater +import com.stepstone.stepper.Step +import org.apache.fineract.ui.adapters.NameListAdapter +import org.apache.fineract.ui.base.FineractBaseActivity +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import butterknife.ButterKnife +import butterknife.OnClick +import butterknife.Optional +import com.stepstone.stepper.VerificationError +import com.wajahatkarim3.easyvalidation.core.view_ktx.validator +import kotlinx.android.synthetic.main.fragment_step_account_review.view.* +import kotlinx.android.synthetic.main.fragment_step_add_account_holder.* +import kotlinx.android.synthetic.main.fragment_step_add_account_holder.view.* +import org.apache.fineract.R +import org.apache.fineract.ui.base.FineractBaseFragment +import org.apache.fineract.ui.online.accounting.accounts.AccountAction +import org.apache.fineract.utils.Constants +import org.apache.fineract.utils.MaterialDialog +import org.apache.fineract.utils.Utils +import javax.inject.Inject + +/* + * Created by Varun Jain on 21/July/2021 +*/ + +class AddAccountHoldersStepFragment : FineractBaseFragment(), Step, NameListAdapter.OnItemClickListener { + + lateinit var rootView: View + var holders: ArrayList = ArrayList() + private var currentAccountAction = AccountAction.CREATE + private var editItemPosition = 0 + private lateinit var accountAction: AccountAction + + @Inject + lateinit var nameListAdapter: NameListAdapter + + companion object { + fun newInstance (accountAction: AccountAction) = + AddAccountHoldersStepFragment().apply { + val bundle = Bundle().apply { + putSerializable(Constants.ACCOUNT_ACTION, accountAction) + } + arguments = bundle + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.getSerializable(Constants.ACCOUNT_ACTION)?.let { + accountAction = it as AccountAction + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + rootView = inflater.inflate(R.layout.fragment_step_add_account_holder, container, false) + ButterKnife.bind(this, rootView) + (activity as FineractBaseActivity).activityComponent.inject(this) + rootView.rvHolderName.adapter = nameListAdapter + nameListAdapter.setOnItemClickListener(this) + if (accountAction == AccountAction.EDIT) { + populateViews() + } + return rootView + } + + private fun populateViews() { + val account = (activity as CreateAccountActivity).getAccount() + holders = account.holders as ArrayList + if (holders.size == 0) { + showRecyclerView(false) + } else { + showRecyclerView(true) + } + nameListAdapter.submitList(holders) + } + + @Optional + @OnClick(R.id.ivAddHolder) + fun showAddHolderView() { + showAddHolderView(AccountAction.CREATE, null) + } + + private fun showAddHolderView(action: AccountAction, name: String?) { + currentAccountAction = action + llAddHolderAccountStep.visibility = View.VISIBLE + when (action) { + AccountAction.CREATE -> { + btnAddHolder.text = getString(R.string.add) + } + AccountAction.EDIT -> { + etNewHolder.setText(name) + btnAddHolder.text = getString(R.string.update) + } + } + } + + @Optional + @OnClick(R.id.btnAddHolder) + fun addHolder() { + if (etNewHolder.validator() + .nonEmpty() + .addErrorCallback { etNewHolder.error = it }.check()) { + if (currentAccountAction == AccountAction.CREATE) { + holders.add(etNewHolder.text.toString().trim { it <= ' ' }) + } else { + holders[editItemPosition] = etNewHolder.text.toString().trim { it <= ' ' } + } + etNewHolder.text.clear() + llAddHolderAccountStep.visibility = View.GONE + Utils.hideKeyboard(context, etNewHolder) + showRecyclerView(true) + nameListAdapter.submitList(holders) + } + } + + fun showRecyclerView(isShow: Boolean) { + if (isShow) { + rootView.rvHolderName.visibility = View.VISIBLE + rootView.tvAddedHolder.visibility = View.GONE + } else { + rootView.rvHolderName.visibility = View.GONE + rootView.tvAddedHolder.visibility = View.VISIBLE + } + } + + @Optional + @OnClick(R.id.btnCancelAddHolder) + fun cancelHolderAddition() { + etNewHolder.text.clear() + llAddHolderAccountStep.visibility = View.GONE + } + + override fun verifyStep(): VerificationError? { + if (holders.size == 0) { + Toast.makeText(context, getString(R.string.error_acc_atleast_1_holder), Toast.LENGTH_SHORT).show() + return VerificationError("") + } + (activity as CreateAccountActivity).setHolders(holders) + return null + } + + override fun onSelected() {} + + override fun onError(p0: VerificationError) {} + + override fun onEditClicked(position: Int) { + editItemPosition = position + showAddHolderView(AccountAction.EDIT, holders[position]) + } + + override fun onDeleteClicked(position: Int) { + MaterialDialog.Builder().init(context).apply { + setTitle(getString(R.string.dialog_title_confirm_deletion)) + setMessage(getString(R.string.dialog_message_confirm_name_deletion, holders[position])) + setPositiveButton(getString(R.string.delete) + ) { dialog: DialogInterface?, _ -> + holders.removeAt(position) + nameListAdapter.submitList(holders) + if (holders.size == 0) { + showRecyclerView(false) + } + dialog?.dismiss() + } + setNegativeButton(getString(R.string.dialog_action_cancel)) + createMaterialDialog() + }.run { show() } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AddAccountSignAuthoritiesFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AddAccountSignAuthoritiesFragment.kt new file mode 100644 index 00000000..cd8c110f --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/AddAccountSignAuthoritiesFragment.kt @@ -0,0 +1,186 @@ +package org.apache.fineract.ui.online.accounting.accounts.createaccount + +import android.content.Context +import android.content.DialogInterface +import android.opengl.Visibility +import android.os.Bundle +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import butterknife.ButterKnife +import butterknife.OnClick +import butterknife.Optional +import com.stepstone.stepper.Step +import com.stepstone.stepper.VerificationError +import com.wajahatkarim3.easyvalidation.core.view_ktx.validator +import kotlinx.android.synthetic.main.fragment_step_account_review.view.* +import kotlinx.android.synthetic.main.fragment_step_add_account_holder.* +import kotlinx.android.synthetic.main.fragment_step_add_group_member.view.* +import kotlinx.android.synthetic.main.fragment_step_add_signature_authorities.* +import kotlinx.android.synthetic.main.fragment_step_add_signature_authorities.view.* +import org.apache.fineract.R +import org.apache.fineract.ui.adapters.NameListAdapter +import org.apache.fineract.ui.base.FineractBaseActivity +import org.apache.fineract.ui.base.FineractBaseFragment +import org.apache.fineract.ui.online.accounting.accounts.AccountAction +import org.apache.fineract.ui.online.groups.GroupAction +import org.apache.fineract.utils.Constants +import org.apache.fineract.utils.MaterialDialog +import org.apache.fineract.utils.Utils +import javax.inject.Inject + + +/* + * Created by Varun Jain on 21/July/2021 +*/ + +class AddAccountSignAuthoritiesFragment : FineractBaseFragment(), Step, NameListAdapter.OnItemClickListener { + + lateinit var rootView: View + var signatureAuthorities: ArrayList = ArrayList() + private var currentAccountAction = AccountAction.CREATE + private var editItemPosition = 0 + private lateinit var accountAction: AccountAction + + @Inject + lateinit var namesListAdapter: NameListAdapter + + companion object { + fun newInstance (accountAction: AccountAction) = + AddAccountSignAuthoritiesFragment().apply { + val bundle = Bundle().apply { + putSerializable(Constants.ACCOUNT_ACTION, accountAction) + } + arguments = bundle + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.getSerializable(Constants.ACCOUNT_ACTION)?.let { + accountAction = it as AccountAction + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + rootView = inflater.inflate(R.layout.fragment_step_add_signature_authorities, container, false) + ButterKnife.bind(this, rootView) + (activity as FineractBaseActivity).activityComponent.inject(this) + rootView.rvAddSignatureAuthoritiesAccountStep.adapter = namesListAdapter + namesListAdapter.setOnItemClickListener(this) + if (accountAction == AccountAction.EDIT) { + showDataOnViews() + } + return rootView + } + + private fun showDataOnViews() { + val account = (activity as CreateAccountActivity).getAccount() + signatureAuthorities = account.signatureAuthorities as ArrayList + if (signatureAuthorities.size == 0) { + showRecyclerView(false) + } else { + showRecyclerView(true) + } + namesListAdapter.submitList(signatureAuthorities) + } + + @Optional + @OnClick(R.id.ivAddSignatureAuthorities) + fun showAddSignatureAuthoritiesView() { + showAddSignatureAuthoritiesView(AccountAction.CREATE, null) + } + + private fun showAddSignatureAuthoritiesView(action: AccountAction, name: String?) { + currentAccountAction = action + llAddSignatureAuthoritiesAccountStep.visibility = View.VISIBLE + when (action) { + AccountAction.CREATE -> { + btnAddSignatureAuthority.text = getString(R.string.add) + } + AccountAction.EDIT -> { + etNewSignatureAuthorities.setText(name) + btnAddSignatureAuthority.text = getString(R.string.update) + } + } + } + + @Optional + @OnClick(R.id.btnAddSignatureAuthority) + fun addSignatureAuthority() { + if (etNewSignatureAuthorities.validator() + .nonEmpty() + .addErrorCallback { etNewSignatureAuthorities.error = it }.check()) { + if (currentAccountAction == AccountAction.CREATE) { + signatureAuthorities.add(etNewSignatureAuthorities.text.toString().trim { it <= ' ' }) + } else { + signatureAuthorities[editItemPosition] = etNewSignatureAuthorities.text.toString().trim { it <= ' ' } + } + etNewSignatureAuthorities.text.clear() + llAddSignatureAuthoritiesAccountStep.visibility = View.GONE + Utils.hideKeyboard(context, etNewSignatureAuthorities) + showRecyclerView(true) + namesListAdapter.submitList(signatureAuthorities) + } + } + + + private fun showRecyclerView(show: Boolean) { + if (show) { + rootView.rvAddSignatureAuthoritiesAccountStep.visibility = View.VISIBLE + rootView.tvAddedSignatureAuthorities.visibility = View.GONE + } else { + rootView.rvAddSignatureAuthoritiesAccountStep.visibility = View.GONE + rootView.tvAddedSignatureAuthorities.visibility = View.VISIBLE + } + } + + @Optional + @OnClick(R.id.btnCancelAddSignatureAuthority) + fun cancelSignatureAuthorityAddition() { + etNewSignatureAuthorities.text.clear() + llAddSignatureAuthoritiesAccountStep.visibility = View.GONE + } + + override fun verifyStep(): VerificationError? { + if (signatureAuthorities.size == 0) { + Toast.makeText(context, getString(R.string.error_acc_atleast_1_signature_authority), Toast.LENGTH_SHORT).show() + return VerificationError("") + } + (activity as CreateAccountActivity).setSignatureAuthorities(signatureAuthorities) + return null + } + + override fun onSelected() {} + + override fun onError(p0: VerificationError) {} + + override fun onEditClicked(position: Int) { + editItemPosition = position + showAddSignatureAuthoritiesView(AccountAction.EDIT, signatureAuthorities[position]) + } + + override fun onDeleteClicked(position: Int) { + MaterialDialog.Builder().init(context).apply { + setTitle(getString(R.string.dialog_title_confirm_deletion)) + setMessage(getString(R.string.dialog_message_confirm_name_deletion, signatureAuthorities[position])) + setPositiveButton(getString(R.string.delete) + ) { dialog: DialogInterface?, _ -> + signatureAuthorities.removeAt(position) + namesListAdapter.submitList(signatureAuthorities) + if (signatureAuthorities.size == 0) { + showRecyclerView(false) + } + dialog?.dismiss() + } + setNegativeButton(getString(R.string.dialog_action_cancel)) + createMaterialDialog() + }.run { show() } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/CreateAccountActivity.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/CreateAccountActivity.kt new file mode 100644 index 00000000..7f8b1c4f --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/CreateAccountActivity.kt @@ -0,0 +1,164 @@ +package org.apache.fineract.ui.online.accounting.accounts.createaccount + +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import com.stepstone.stepper.StepperLayout +import com.stepstone.stepper.VerificationError +import kotlinx.android.synthetic.main.activity_create_account.* +import kotlinx.android.synthetic.main.activity_create_group.* +import org.apache.fineract.R +import org.apache.fineract.data.Status +import org.apache.fineract.data.models.accounts.Account +import org.apache.fineract.data.models.accounts.AccountType +import org.apache.fineract.ui.base.FineractBaseActivity +import org.apache.fineract.ui.base.Toaster +import org.apache.fineract.ui.online.accounting.accounts.AccountAction +import org.apache.fineract.ui.online.accounting.accounts.viewmodel.AccountsViewModel +import org.apache.fineract.ui.online.accounting.accounts.viewmodel.AccountsViewModelFactory +import org.apache.fineract.utils.Constants +import javax.inject.Inject + +/* + * Created by Varun Jain on 20/July/2021 +*/ + +class CreateAccountActivity : FineractBaseActivity(), StepperLayout.StepperListener { + + private var account = Account() + private var accountAction = AccountAction.CREATE + + + @Inject + lateinit var accountsViewModelFactory: AccountsViewModelFactory + + lateinit var accountsViewModel: AccountsViewModel + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_create_account) + activityComponent.inject(this) + accountAction = intent.getSerializableExtra(Constants.ACCOUNT_ACTION) as AccountAction + when (accountAction) { + AccountAction.CREATE -> { + setToolbarTitle(getString(R.string.create_account)) + } + AccountAction.EDIT -> { + setToolbarTitle(getString(R.string.edit_account)) + intent?.extras?.getParcelable(Constants.ACCOUNT)?.let { + account = it + } + } + } + accountsViewModel = + ViewModelProviders.of(this, accountsViewModelFactory).get(AccountsViewModel::class.java) + subscribeUI() + showBackButton() + slCreateAccount.adapter = CreateAccountAdapter(supportFragmentManager, this, accountAction) + slCreateAccount.setOffscreenPageLimit(slCreateAccount.adapter.count) + slCreateAccount.setListener(this) + } + + override fun onCompleted(p0: View?) { + when (accountAction) { + AccountAction.CREATE -> account.identifier?.let { + accountsViewModel.createAccount(account) + } + AccountAction.EDIT -> account.identifier?.let { + accountsViewModel.updateAccount(account) + } + } + } + + override fun onError(verificationError: VerificationError?) {} + + override fun onStepSelected(newStepPosition: Int) {} + + override fun onReturn() {} + + + private fun subscribeUI() { + accountsViewModel.status.observe(this, Observer { status -> + when (status) { + Status.LOADING -> + if (accountAction == AccountAction.CREATE) { + showMifosProgressDialog(getString(R.string.creating_account_please_wait)) + } else { + showMifosProgressDialog(getString(R.string.updating_account_please_wait)) + } + Status.ERROR -> { + hideMifosProgressDialog() + if (accountAction == AccountAction.CREATE) { + Toaster.show( + findViewById(android.R.id.content), + R.string.error_while_creating_account, + Toast.LENGTH_SHORT + ) + } else { + Toaster.show( + findViewById(android.R.id.content), + R.string.error_while_updating_account, + Toast.LENGTH_SHORT + ) + } + } + Status.DONE -> { + hideMifosProgressDialog() + if (accountAction == AccountAction.CREATE) { + Toast.makeText( + this, + getString( + R.string.account_identifier_created_successfully, + account.identifier + ), + Toast.LENGTH_SHORT + ).show() + } else { + Toast.makeText( + this, + getString( + R.string.account_identifier_updated_successfully, + account.identifier + ), + Toast.LENGTH_SHORT + ).show() + } + finish() + } + } + }) + } + + fun setHolders(holders: List) { + account.holders = holders + } + + fun setSignatureAuthorities(setSignatureAuthorities: List) { + account.signatureAuthorities = setSignatureAuthorities + } + + fun setAccountDetails( + identifier: String, + balance: Double, + type: AccountType, + alternativeAccountNum: String, + name: String, + ledger: String, + refAccount: String + ) { + account.identifier = identifier + account.balance = balance + account.type = type + account.alternativeAccountNumber = alternativeAccountNum + account.name = name + account.ledger = ledger + account.referenceAccount = refAccount + } + + fun getAccount(): Account { + return account + } +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/CreateAccountAdapter.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/CreateAccountAdapter.kt new file mode 100644 index 00000000..b48cd998 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/createaccount/CreateAccountAdapter.kt @@ -0,0 +1,39 @@ +package org.apache.fineract.ui.online.accounting.accounts.createaccount + +import android.content.Context +import androidx.fragment.app.FragmentManager +import com.stepstone.stepper.Step +import com.stepstone.stepper.adapter.AbstractFragmentStepAdapter +import com.stepstone.stepper.viewmodel.StepViewModel +import org.apache.fineract.R +import org.apache.fineract.ui.online.accounting.accounts.AccountAction + +/* + * Created by Varun Jain on 21/July/2021 +*/ + +class CreateAccountAdapter constructor(fm: FragmentManager, + context: Context, + val accountAction: AccountAction +) : AbstractFragmentStepAdapter(fm, context) { + + private var createAccountSteps = context.resources.getStringArray(R.array.create_account_steps) + + override fun getCount(): Int { + return createAccountSteps.size + } + + override fun createStep(position: Int): Step? { + when (position) { + 0 -> return AccountDetailsStepFragment.newInstance(accountAction) + 1 -> return AddAccountHoldersStepFragment.newInstance(accountAction) + 2 -> return AddAccountSignAuthoritiesFragment.newInstance(accountAction) + 3 -> return AccountReviewStepFragment.newInstance() + } + return null + } + + override fun getViewModel(position: Int): StepViewModel { + return StepViewModel.Builder(context).setTitle(createAccountSteps[position]).create() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/viewmodel/AccountsViewModel.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/viewmodel/AccountsViewModel.kt new file mode 100644 index 00000000..803511a1 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/viewmodel/AccountsViewModel.kt @@ -0,0 +1,167 @@ +package org.apache.fineract.ui.online.accounting.accounts.viewmodel + +import org.apache.fineract.data.models.accounts.Account +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.observers.DisposableCompletableObserver +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.DataManagerAccounting +import org.apache.fineract.data.datamanager.api.DataManagerAnonymous +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.ui.online.accounting.accounts.accounttasks.AccountCommand +import org.apache.fineract.utils.DateUtils +import org.apache.fineract.utils.serializeToMap +import org.apache.fineract.utils.toDataClass + +/* + * Created by Varun Jain on 18/July/2021 +*/ + +class AccountsViewModel constructor(private val synchronizationManager: SynchronizationManager, + private val dataManagerAnonymous: DataManagerAnonymous, + private val preferencesHelper: PreferencesHelper, + private val dataManagerAccounting: DataManagerAccounting +) : ViewModel() { + + var accountsList = MutableLiveData>() + 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() + val status: LiveData + get() = _status + + @SuppressLint("CheckResult") + fun getAccounts(): MutableLiveData>? { + val expression = Expression.property("documentType") + .equalTo(Expression.string(DocumentType.ACCOUNT.value)) + .and(Expression.property("state").notEqualTo(Expression.string("null"))) + val hashMapList = synchronizationManager.getDocuments(expression) + if (hashMapList?.isEmpty() == null) { + return null + } + val list = arrayListOf() + for (map in hashMapList) { + list.add(map.toDataClass()) + } + accountsList.value = list + + return accountsList + } + + fun searchAccount( + accounts: ArrayList, + query: String, + searchedAccount: (ArrayList) -> Unit + ) { + searchedAccount( + ArrayList( + Observable.fromIterable(accounts).filter { account -> + account.identifier?.toLowerCase()?.contains(query.toLowerCase()).toString() + .toBoolean() + }.toList().blockingGet() + ) + ) + } + + @SuppressLint("CheckResult") + fun createAccount(account: Account) { + uiScope.launch { + withContext(Dispatchers.Main) { + try { + _status.value = Status.LOADING + account.createdBy = preferencesHelper.userName + account.createdOn = DateUtils.getCurrentDate() + account.lastModifiedBy = preferencesHelper.userName + account.lastModifiedOn = DateUtils.getCurrentDate() + account.state = Account.State.OPEN + synchronizationManager.saveDocument(account.identifier!!, account.serializeToMap()) + _status.value = Status.DONE + } catch (exception: Exception) { + _status.value = Status.ERROR + } + } + } + } + + fun updateAccount(account: Account) { + uiScope.launch { + withContext(Dispatchers.Main) { + try { + _status.value = Status.LOADING + account.createdBy = preferencesHelper.userName + account.createdOn = DateUtils.getCurrentDate() + account.lastModifiedBy = preferencesHelper.userName + account.lastModifiedOn = DateUtils.getCurrentDate() + account.identifier?.let { + synchronizationManager.updateDocument(account.identifier!!, account.serializeToMap()) + _status.value = Status.DONE + } + } catch (exception: Exception) { + _status.value = Status.ERROR + } + } + } + } + + @SuppressLint("CheckResult") + fun deleteAccount(account: Account) { + uiScope.launch { + withContext(Dispatchers.Main) { + try { + _status.value = Status.LOADING + account.lastModifiedBy = preferencesHelper.userName + account.lastModifiedOn = DateUtils.getCurrentDate() + account.state = Account.State.CLOSED + synchronizationManager.deleteDocument(account.identifier!!) + _status.value = Status.DONE + } catch (exception: Exception) { + _status.value = Status.ERROR + } + } + } + } + + fun changeAccountStatus(identifier: String, account: Account, command: AccountCommand) { + uiScope.launch { + withContext(Dispatchers.Main) { + try { + _status.value = Status.LOADING + when (command.action) { + AccountCommand.AccountTaskAction.UNLOCK -> account.state = Account.State.OPEN + AccountCommand.AccountTaskAction.REOPEN -> account.state = Account.State.OPEN + AccountCommand.AccountTaskAction.LOCK -> account.state = Account.State.LOCKED + AccountCommand.AccountTaskAction.CLOSE -> account.state = Account.State.CLOSED + else -> account.state = Account.State.OPEN + } + synchronizationManager.updateDocument(identifier, account.serializeToMap()) + _status.value = Status.DONE + } catch (e: Exception) { + _status.value = Status.ERROR + } + } + } + } + + /** + * When the [ViewModel] is finished, we cancel our coroutine [viewModelJob], which tells the + * Retrofit service to stop. + */ + 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/accounting/accounts/viewmodel/AccountsViewModelFactory.kt b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/viewmodel/AccountsViewModelFactory.kt new file mode 100644 index 00000000..cfe49db2 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/ui/online/accounting/accounts/viewmodel/AccountsViewModelFactory.kt @@ -0,0 +1,31 @@ +package org.apache.fineract.ui.online.accounting.accounts.viewmodel + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import org.apache.fineract.couchbase.SynchronizationManager +import org.apache.fineract.data.datamanager.DataManagerAccounting +import org.apache.fineract.data.datamanager.api.DataManagerAnonymous +import org.apache.fineract.data.local.PreferencesHelper +import org.apache.fineract.injection.ApplicationContext +import javax.inject.Inject + + +/* + * Created by Varun Jain on 18/July/2021 +*/ + + +class AccountsViewModelFactory @Inject constructor(@ApplicationContext var context: Context, + private val synchronizationManager: SynchronizationManager, + private val dataManagerAnonymous: DataManagerAnonymous, + private val dataManagerAccounting: DataManagerAccounting, + private val preferencesHelper: PreferencesHelper +) + : ViewModelProvider.NewInstanceFactory() { + + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + return AccountsViewModel(synchronizationManager, dataManagerAnonymous, preferencesHelper, dataManagerAccounting) as T + } +} \ No newline at end of file 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 072c90f3..c8e4fc35 100644 --- a/app/src/main/java/org/apache/fineract/utils/Constants.kt +++ b/app/src/main/java/org/apache/fineract/utils/Constants.kt @@ -12,4 +12,6 @@ object Constants { const val BASIC_AUTH_KEY = "basic_auth_key" const val GATEWAY_USER_NAME = "fineract-cn" const val GATEWAY_PASSWORD = "password" + const val ACCOUNT = "account" + const val ACCOUNT_ACTION = "account_Action" } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_create_account.xml b/app/src/main/res/layout/activity_create_account.xml new file mode 100644 index 00000000..7ec89587 --- /dev/null +++ b/app/src/main/res/layout/activity_create_account.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_account_details.xml b/app/src/main/res/layout/fragment_account_details.xml new file mode 100644 index 00000000..b4507996 --- /dev/null +++ b/app/src/main/res/layout/fragment_account_details.xml @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_account_tasks_bottom_sheet.xml b/app/src/main/res/layout/fragment_account_tasks_bottom_sheet.xml new file mode 100644 index 00000000..db0a3e88 --- /dev/null +++ b/app/src/main/res/layout/fragment_account_tasks_bottom_sheet.xml @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +