From b97e977fae7693a3de46f44e8d65cf99916aa024 Mon Sep 17 00:00:00 2001 From: Avneet Singh Date: Mon, 19 Feb 2024 03:44:07 +0530 Subject: [PATCH] refactor #2516: migrated loan account details screen to compose --- .../LoanAccountContainerActivity.kt | 3 +- .../SavingsAccountContainerActivity.kt | 12 - .../mobile/ui/activities/base/BaseActivity.kt | 10 + .../ui/fragments/ClientChargeFragment.kt | 3 +- .../ui/fragments/GuarantorListFragment.kt | 1 + .../fragments/LoanAccountSummaryFragment.kt | 2 + .../LoanAccountTransactionFragment.kt | 2 + .../fragments/LoanAccountWithdrawFragment.kt | 2 + .../fragments/LoanAccountsDetailFragment.kt | 408 ------------------ .../ui/fragments/LoanApplicationFragment.kt | 1 + .../LoanRepaymentScheduleFragment.kt | 2 + .../ui/fragments/QrCodeDisplayFragment.kt | 3 +- .../fragments/SavingsMakeTransferFragment.kt | 2 +- .../loan_account/LoanAccountDetailContent.kt | 237 ++++++++++ .../loan_account/LoanAccountDetailScreen.kt | 117 +++++ .../loan_account/LoanAccountDetailTopBar.kt | 99 +++++ .../LoanAccountsDetailFragment.kt | 227 ++++++++++ .../LoanAccountsDetailViewModel.kt | 61 +++ .../viewModels/LoanAccountsDetailViewModel.kt | 37 -- .../layout/fragment_loan_account_details.xml | 2 +- .../LoanAccountsDetailViewModelTest.kt | 1 + 21 files changed, 770 insertions(+), 462 deletions(-) delete mode 100644 app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountsDetailFragment.kt create mode 100644 app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailContent.kt create mode 100644 app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailScreen.kt create mode 100644 app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailTopBar.kt create mode 100644 app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailFragment.kt create mode 100644 app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailViewModel.kt delete mode 100644 app/src/main/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModel.kt diff --git a/app/src/main/java/org/mifos/mobile/ui/activities/LoanAccountContainerActivity.kt b/app/src/main/java/org/mifos/mobile/ui/activities/LoanAccountContainerActivity.kt index ec07194fe..39e933a2c 100644 --- a/app/src/main/java/org/mifos/mobile/ui/activities/LoanAccountContainerActivity.kt +++ b/app/src/main/java/org/mifos/mobile/ui/activities/LoanAccountContainerActivity.kt @@ -1,10 +1,11 @@ package org.mifos.mobile.ui.activities import android.os.Bundle +import android.view.View import org.mifos.mobile.R import org.mifos.mobile.databinding.ActivityContainerBinding import org.mifos.mobile.ui.activities.base.BaseActivity -import org.mifos.mobile.ui.fragments.LoanAccountsDetailFragment +import org.mifos.mobile.ui.loan_account.LoanAccountsDetailFragment import org.mifos.mobile.utils.Constants /* diff --git a/app/src/main/java/org/mifos/mobile/ui/activities/SavingsAccountContainerActivity.kt b/app/src/main/java/org/mifos/mobile/ui/activities/SavingsAccountContainerActivity.kt index 14637aa44..7367d0a61 100644 --- a/app/src/main/java/org/mifos/mobile/ui/activities/SavingsAccountContainerActivity.kt +++ b/app/src/main/java/org/mifos/mobile/ui/activities/SavingsAccountContainerActivity.kt @@ -36,18 +36,6 @@ class SavingsAccountContainerActivity : BaseActivity() { } } - fun hideToolbar() { - binding.apply { - toolbar?.visibility = View.GONE - } - } - - fun showToolbar() { - binding.apply { - toolbar?.visibility = View.VISIBLE - } - } - companion object { var transferSuccess = false } diff --git a/app/src/main/java/org/mifos/mobile/ui/activities/base/BaseActivity.kt b/app/src/main/java/org/mifos/mobile/ui/activities/base/BaseActivity.kt index 3a166f66c..cae4d31d2 100644 --- a/app/src/main/java/org/mifos/mobile/ui/activities/base/BaseActivity.kt +++ b/app/src/main/java/org/mifos/mobile/ui/activities/base/BaseActivity.kt @@ -51,6 +51,16 @@ open class BaseActivity : BasePassCodeActivity(), BaseActivityCallback { } } + fun showToolbar() { + toolbar = findViewById(R.id.toolbar) + toolbar?.visibility = View.VISIBLE + } + + fun hideToolbar() { + toolbar = findViewById(R.id.toolbar) + toolbar?.visibility = View.GONE + } + /** * Used for removing elevation from toolbar */ diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/ClientChargeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/ClientChargeFragment.kt index 30f47da4b..6bd6541b1 100644 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/ClientChargeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/fragments/ClientChargeFragment.kt @@ -18,6 +18,7 @@ import org.mifos.mobile.R import org.mifos.mobile.databinding.FragmentClientChargeBinding import org.mifos.mobile.models.Charge import org.mifos.mobile.ui.activities.SavingsAccountContainerActivity +import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.adapters.ClientChargeAdapter import org.mifos.mobile.ui.enums.ChargeType import org.mifos.mobile.ui.fragments.base.BaseFragment @@ -49,7 +50,7 @@ class ClientChargeFragment : BaseFragment() { private var sweetUIErrorHandler: SweetUIErrorHandler? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - (activity as? SavingsAccountContainerActivity)?.showToolbar() + (activity as? BaseActivity)?.showToolbar() if (arguments != null) { id = arguments?.getLong(Constants.CLIENT_ID) chargeType = arguments?.getCheckedSerializable( diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/GuarantorListFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/GuarantorListFragment.kt index c0f06a3f0..6d8bd5d69 100644 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/GuarantorListFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/fragments/GuarantorListFragment.kt @@ -47,6 +47,7 @@ class GuarantorListFragment : BaseFragment() { private var disposableDeleteGuarantor: Disposable? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + (activity as? BaseActivity)?.showToolbar() if (arguments != null) loanId = requireArguments().getLong(Constants.LOAN_ID) } diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountSummaryFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountSummaryFragment.kt index f286511ae..6c6f48be8 100644 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountSummaryFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountSummaryFragment.kt @@ -8,6 +8,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.mifos.mobile.R import org.mifos.mobile.databinding.FragmentLoanAccountSummaryBinding import org.mifos.mobile.models.accounts.loan.LoanWithAssociations +import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.utils.Constants import org.mifos.mobile.utils.CurrencyUtil @@ -26,6 +27,7 @@ class LoanAccountSummaryFragment : BaseFragment() { private var loanWithAssociations: LoanWithAssociations? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + (activity as? BaseActivity)?.showToolbar() if (arguments != null) { loanWithAssociations = arguments?.getCheckedParcelable(LoanWithAssociations::class.java, Constants.LOAN_ACCOUNT) } diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountTransactionFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountTransactionFragment.kt index 8b7cdfd48..190dda8ce 100644 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountTransactionFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountTransactionFragment.kt @@ -19,6 +19,7 @@ import kotlinx.coroutines.launch import org.mifos.mobile.R import org.mifos.mobile.databinding.FragmentLoanAccountTransactionsBinding import org.mifos.mobile.models.accounts.loan.LoanWithAssociations +import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.adapters.RecentTransactionListAdapter import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.utils.Constants @@ -50,6 +51,7 @@ class LoanAccountTransactionFragment : BaseFragment() { private var sweetUIErrorHandler: SweetUIErrorHandler? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + (activity as? BaseActivity)?.showToolbar() if (arguments != null) { loanId = arguments?.getLong(Constants.LOAN_ID) } diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountWithdrawFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountWithdrawFragment.kt index ee04917d0..47f2c8979 100644 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountWithdrawFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountWithdrawFragment.kt @@ -14,6 +14,7 @@ import org.mifos.mobile.R import org.mifos.mobile.databinding.FragmentLoanWithdrawBinding import org.mifos.mobile.models.accounts.loan.LoanWithAssociations import org.mifos.mobile.models.accounts.loan.LoanWithdraw +import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.utils.Constants import org.mifos.mobile.utils.DateHelper @@ -36,6 +37,7 @@ class LoanAccountWithdrawFragment : BaseFragment() { private var loanWithAssociations: LoanWithAssociations? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + (activity as? BaseActivity)?.showToolbar() if (arguments != null) { loanWithAssociations = arguments?.getCheckedParcelable(LoanWithAssociations::class.java, Constants.LOAN_ACCOUNT) } diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountsDetailFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountsDetailFragment.kt deleted file mode 100644 index 9fcdcdd26..000000000 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanAccountsDetailFragment.kt +++ /dev/null @@ -1,408 +0,0 @@ -package org.mifos.mobile.ui.fragments - -import android.os.Bundle -import android.os.Parcelable -import android.view.* -import android.widget.Toast -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import com.github.therajanmaurya.sweeterror.SweetUIErrorHandler -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch -import org.mifos.mobile.R -import org.mifos.mobile.api.local.PreferencesHelper -import org.mifos.mobile.databinding.FragmentLoanAccountDetailsBinding -import org.mifos.mobile.models.accounts.loan.LoanWithAssociations -import org.mifos.mobile.ui.activities.base.BaseActivity -import org.mifos.mobile.ui.enums.AccountType -import org.mifos.mobile.ui.enums.ChargeType -import org.mifos.mobile.ui.enums.LoanState -import org.mifos.mobile.ui.fragments.base.BaseFragment -import org.mifos.mobile.utils.* -import org.mifos.mobile.utils.ParcelableAndSerializableUtils.getCheckedParcelable -import org.mifos.mobile.viewModels.LoanAccountsDetailViewModel -import javax.inject.Inject - -/* -~This project is licensed under the open source MPL V2. -~See https://github.com/openMF/self-service-app/blob/master/LICENSE.md -*/ /** - * @author Vishwajeet - * @since 19/08/16 - */ -@AndroidEntryPoint -class LoanAccountsDetailFragment : BaseFragment() { - private var _binding: FragmentLoanAccountDetailsBinding? = null - private val binding get() = _binding!! - - private val viewModel: LoanAccountsDetailViewModel by viewModels() - - @JvmField - @Inject - var preferencesHelper: PreferencesHelper? = null - private var loanWithAssociations: LoanWithAssociations? = null - private var showLoanUpdateOption = false - private var loanId: Long? = 0 - private var sweetUIErrorHandler: SweetUIErrorHandler? = null - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - if (arguments != null) { - loanId = arguments?.getLong(Constants.LOAN_ID) - } - setHasOptionsMenu(true) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View { - _binding = FragmentLoanAccountDetailsBinding.inflate(inflater, container, false) - val rootView = binding.root - setToolbarTitle(getString(R.string.loan_account_details)) - sweetUIErrorHandler = SweetUIErrorHandler(activity, rootView) - if (savedInstanceState == null && this.loanWithAssociations == null) { - viewModel.loadLoanAccountDetails(loanId) - } else { - showLoanAccountsDetail(this.loanWithAssociations) - } - return rootView - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putParcelable(Constants.LOAN_ACCOUNT, loanWithAssociations) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - if (savedInstanceState != null) { - showLoanAccountsDetail(savedInstanceState.getCheckedParcelable(LoanWithAssociations::class.java, Constants.LOAN_ACCOUNT)) - } - } - - /** - * Shows details about loan account fetched from server is status is Active else shows and - * error layout i.e. `layoutError` with a msg related to the status. - * - * @param loanWithAssociations object containing details of each loan account, - */ - private fun showLoanAccountsDetail(loanWithAssociations: LoanWithAssociations?) { - this.loanWithAssociations = loanWithAssociations - with(binding) { - llAccountDetail.visibility = View.VISIBLE - if (loanWithAssociations?.status?.active!!) { - val overdueSinceDate = loanWithAssociations.summary?.getOverdueSinceDate() - if (overdueSinceDate == null) { - tvDueDate.setText(R.string.not_available) - } else { - tvDueDate.text = DateHelper.getDateAsString(overdueSinceDate) - } - showDetails(loanWithAssociations) - } else if (loanWithAssociations.status?.pendingApproval!!) { - sweetUIErrorHandler?.showSweetCustomErrorUI( - getString(R.string.approval_pending), - R.drawable.ic_assignment_turned_in_black_24dp, - llAccountDetail, - layoutError.root, - ) - showLoanUpdateOption = true - } else if (loanWithAssociations.status?.waitingForDisbursal!!) { - sweetUIErrorHandler?.showSweetCustomErrorUI( - getString(R.string.waiting_for_disburse), - R.drawable.ic_assignment_turned_in_black_24dp, - llAccountDetail, - layoutError.root, - ) - } else { - btnMakePayment.visibility = View.GONE - tvDueDate.setText(R.string.not_available) - showDetails(loanWithAssociations) - } - } - activity?.invalidateOptionsMenu() - } - - /** - * Sets basic information about a loan - * - * @param loanWithAssociations object containing details of each loan account, - */ - private fun showDetails(loanWithAssociations: LoanWithAssociations?) { - with(binding) { - tvOutstandingBalance.text = resources.getString( - R.string.string_and_string, - loanWithAssociations?.summary?.currency?.displaySymbol, - CurrencyUtil.formatCurrency( - activity, - loanWithAssociations?.summary?.totalOutstanding, - ), - ) - if (loanWithAssociations?.repaymentSchedule?.periods != null) { - for ((_, _, dueDate, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, totalDueForPeriod) in loanWithAssociations.repaymentSchedule?.periods!!) { - if (dueDate == loanWithAssociations.summary?.getOverdueSinceDate()) { - tvNextInstallment.text = resources.getString( - R.string.string_and_string, - loanWithAssociations.summary?.currency?.displaySymbol, - CurrencyUtil.formatCurrency( - activity, - totalDueForPeriod, - ), - ) - break - } else if (loanWithAssociations.summary?.getOverdueSinceDate() == null) { - tvNextInstallment.setText(R.string.not_available) - } - } - } - tvAccountNumber.text = loanWithAssociations?.accountNo - tvLoanType.text = loanWithAssociations?.loanType?.value - tvCurrency.text = loanWithAssociations?.summary?.currency?.code - } - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.loanUiState.collect { - when (it) { - is LoanUiState.Loading -> showProgress() - is LoanUiState.ShowError -> { - hideProgress() - showErrorFetchingLoanAccountsDetail(getString(it.message)) - } - is LoanUiState.ShowLoan -> { - hideProgress() - showLoanAccountsDetail(it.loanWithAssociations) - } - else -> throw IllegalStateException("Unexpected state: $it") - } - } - } - } - - with(binding) { - btnMakePayment.setOnClickListener { - onMakePaymentClicked() - } - - llSummary.setOnClickListener { - onLoanSummaryClicked() - } - - llRepayment.setOnClickListener { - onRepaymentScheduleClicked() - } - - llLoanTransactions.setOnClickListener { - onTransactionsClicked() - } - - llLoanCharges.setOnClickListener { - chargesClicked() - } - - llLoanQrCode.setOnClickListener { - onQrCodeClicked() - } - - layoutError.btnTryAgain.setOnClickListener { - retryClicked() - } - } - } - - /** - * Opens [SavingsMakeTransferFragment] to Make a payment for loan account with given - * `loanId` - */ - private fun onMakePaymentClicked() { - (activity as BaseActivity?)?.replaceFragment( - SavingsMakeTransferFragment.newInstance( - loanId, - loanWithAssociations?.summary?.totalOutstanding, - Constants.TRANSFER_PAY_TO, - ), - true, - R.id.container, - ) - } - - /** - * Opens [LoanAccountSummaryFragment] - */ - private fun onLoanSummaryClicked() { - (activity as BaseActivity?)?.replaceFragment( - LoanAccountSummaryFragment.newInstance( - loanWithAssociations, - ), - true, - R.id.container, - ) - } - - /** - * Opens [LoanRepaymentScheduleFragment] - */ - private fun onRepaymentScheduleClicked() { - (activity as BaseActivity?)?.replaceFragment( - LoanRepaymentScheduleFragment.newInstance( - loanId, - ), - true, - R.id.container, - ) - } - - /** - * Opens [LoanAccountTransactionFragment] - */ - private fun onTransactionsClicked() { - (activity as BaseActivity?)?.replaceFragment( - LoanAccountTransactionFragment.newInstance( - loanId, - ), - true, - R.id.container, - ) - } - - private fun chargesClicked() { - (activity as BaseActivity?)?.replaceFragment( - ClientChargeFragment.newInstance( - loanWithAssociations?.id?.toLong(), - ChargeType.LOAN, - ), - true, - R.id.container, - ) - } - - private fun onQrCodeClicked() { - val accountDetailsInJson = QrCodeGenerator.getAccountDetailsInString( - loanWithAssociations?.accountNo, - preferencesHelper?.officeName, - AccountType.LOAN, - ) - (activity as BaseActivity?)?.replaceFragment( - QrCodeDisplayFragment.newInstance( - accountDetailsInJson, - ), - true, - R.id.container, - ) - } - - /** - * It is called whenever any error occurs while executing a request - * - * @param message Error message that tells the user about the problem. - */ - fun showErrorFetchingLoanAccountsDetail(message: String?) { - if (!Network.isConnected(activity)) { - sweetUIErrorHandler?.showSweetNoInternetUI( - binding.llAccountDetail, - binding.layoutError.root, - ) - } else { - sweetUIErrorHandler?.showSweetErrorUI( - message, - binding.llAccountDetail, - binding.layoutError.root, - ) - Toast.makeText(activity, message, Toast.LENGTH_SHORT).show() - } - } - - private fun retryClicked() { - if (Network.isConnected(context)) { - sweetUIErrorHandler?.hideSweetErrorLayoutUI( - binding.llAccountDetail, - binding.layoutError.root, - ) - viewModel.loadLoanAccountDetails(loanId) - } else { - Toast.makeText( - context, - getString(R.string.internet_not_connected), - Toast.LENGTH_SHORT, - ).show() - } - } - - fun showProgress() { - showProgressBar() - } - - fun hideProgress() { - hideProgressBar() - } - - override fun onDestroyView() { - super.onDestroyView() - hideProgressBar() - _binding = null - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - super.onCreateOptionsMenu(menu, inflater) - inflater.inflate(R.menu.menu_loan_details, menu) - if (showLoanUpdateOption) { - menu.findItem(R.id.menu_update_loan).isVisible = true - menu.findItem(R.id.menu_withdraw_loan).isVisible = true - menu.findItem(R.id.menu_view_guarantor).isVisible = true - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.menu_update_loan -> { - (activity as BaseActivity?)?.replaceFragment( - LoanApplicationFragment.newInstance( - LoanState.UPDATE, - loanWithAssociations, - ), - true, - R.id.container, - ) - return true - } - - R.id.menu_withdraw_loan -> { - (activity as BaseActivity?)?.replaceFragment( - LoanAccountWithdrawFragment.newInstance( - loanWithAssociations, - ), - true, - R.id.container, - ) - } - - R.id.menu_view_guarantor -> { - (activity as BaseActivity?)?.replaceFragment( - GuarantorListFragment.newInstance( - loanId, - ), - true, - R.id.container, - ) - } - } - return super.onOptionsItemSelected(item) - } - - companion object { - @JvmStatic - fun newInstance(loanId: Long): LoanAccountsDetailFragment { - val fragment = LoanAccountsDetailFragment() - val args = Bundle() - args.putLong(Constants.LOAN_ID, loanId) - fragment.arguments = args - return fragment - } - } -} diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanApplicationFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanApplicationFragment.kt index 9dead7b45..91af7823f 100644 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanApplicationFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanApplicationFragment.kt @@ -100,6 +100,7 @@ class LoanApplicationFragment : BaseFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + (activity as? BaseActivity)?.showToolbar() if (arguments != null) { loanState = arguments?.getCheckedSerializable( LoanState::class.java, diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanRepaymentScheduleFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanRepaymentScheduleFragment.kt index e4d095326..c172a262e 100644 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/LoanRepaymentScheduleFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/fragments/LoanRepaymentScheduleFragment.kt @@ -21,6 +21,7 @@ import org.mifos.mobile.models.accounts.loan.Periods import org.mifos.mobile.models.accounts.loan.tableview.Cell import org.mifos.mobile.models.accounts.loan.tableview.ColumnHeader import org.mifos.mobile.models.accounts.loan.tableview.RowHeader +import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.adapters.LoanRepaymentScheduleAdapter import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.utils.Constants @@ -50,6 +51,7 @@ class LoanRepaymentScheduleFragment : BaseFragment() { private var loanWithAssociations: LoanWithAssociations? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + (activity as? BaseActivity)?.showToolbar() setToolbarTitle(getString(R.string.loan_repayment_schedule)) if (arguments != null) loanId = arguments?.getLong(Constants.LOAN_ID) } diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/QrCodeDisplayFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/QrCodeDisplayFragment.kt index e3b7bf290..d7410b431 100644 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/QrCodeDisplayFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/fragments/QrCodeDisplayFragment.kt @@ -13,6 +13,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.mifos.mobile.R import org.mifos.mobile.databinding.FragmentQrCodeDisplayBinding import org.mifos.mobile.ui.activities.SavingsAccountContainerActivity +import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.utils.Constants import org.mifos.mobile.utils.QrCodeGenerator @@ -30,7 +31,7 @@ class QrCodeDisplayFragment : BaseFragment() { private var json: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - (activity as? SavingsAccountContainerActivity)?.showToolbar() + (activity as? BaseActivity)?.showToolbar() if (arguments != null) { json = arguments?.getString(Constants.QR_DATA) } diff --git a/app/src/main/java/org/mifos/mobile/ui/fragments/SavingsMakeTransferFragment.kt b/app/src/main/java/org/mifos/mobile/ui/fragments/SavingsMakeTransferFragment.kt index cba514aeb..2b494b2d1 100644 --- a/app/src/main/java/org/mifos/mobile/ui/fragments/SavingsMakeTransferFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/fragments/SavingsMakeTransferFragment.kt @@ -61,7 +61,7 @@ class SavingsMakeTransferFragment : BaseFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - (activity as? SavingsAccountContainerActivity)?.showToolbar() + (activity as? BaseActivity)?.showToolbar() if (arguments != null) { accountId = arguments?.getLong(Constants.ACCOUNT_ID) transferType = arguments?.getString(Constants.TRANSFER_TYPE) diff --git a/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailContent.kt b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailContent.kt new file mode 100644 index 000000000..a018ddd0f --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailContent.kt @@ -0,0 +1,237 @@ +package org.mifos.mobile.ui.loan_account + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.mifos.mobile.R +import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.models.accounts.loan.LoanWithAssociations +import org.mifos.mobile.ui.savings_account.DetailField +import org.mifos.mobile.ui.savings_account.MonitorListItemWithIcon +import org.mifos.mobile.utils.CurrencyUtil +import org.mifos.mobile.utils.DateHelper + +@Composable +fun LoanAccountDetailContent( + loanWithAssociations: LoanWithAssociations, + viewLoanSummary: () -> Unit, + viewCharges: () -> Unit, + viewRepaymentSchedule: () -> Unit, + viewTransactions: () -> Unit, + viewQr: () -> Unit, + makePayment: () -> Unit +) { + val scrollState = rememberScrollState() + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(scrollState) + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { + LoanAccountDetailsCard( + loanWithAssociations = loanWithAssociations, + makePayment = makePayment + ) + + Spacer(modifier = Modifier.height(16.dp)) + + LoanMonitorComponent( + viewLoanSummary = viewLoanSummary, + viewCharges = viewCharges, + viewRepaymentSchedule = viewRepaymentSchedule, + viewTransactions = viewTransactions, + viewQr = viewQr + ) + } +} + + +@Composable +fun LoanAccountDetailsCard( + modifier: Modifier = Modifier, + loanWithAssociations: LoanWithAssociations, + makePayment: () -> Unit +) { + val context = LocalContext.current + val isActive = loanWithAssociations.status?.active == true + val currencySymbol = loanWithAssociations.summary?.currency?.displaySymbol ?: "$" + + val dueDate = if (isActive) { + val overdueSinceDate = loanWithAssociations.summary?.getOverdueSinceDate() + overdueSinceDate?.let { DateHelper.getDateAsString(it) } + ?: stringResource(R.string.not_available) + } else { + stringResource(R.string.not_available) + } + + val nextInstallment = getNextInstallment(loanWithAssociations, currencySymbol) + + OutlinedCard(modifier = modifier) { + Column(modifier = Modifier.padding(14.dp)) { + DetailField( + title = stringResource(id = R.string.outstanding_balance), + description = stringResource( + R.string.string_and_string, + currencySymbol, + CurrencyUtil.formatCurrency( + context = context, + amt = loanWithAssociations.summary?.totalOutstanding, + ), + ), + descriptionStyle = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + DetailField( + title = stringResource(id = R.string.next_installment), + description = nextInstallment, + descriptionStyle = MaterialTheme.typography.bodyLarge, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + DetailField( + title = stringResource(id = R.string.due_date), + description = dueDate, + descriptionStyle = MaterialTheme.typography.bodyLarge, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + DetailField( + title = stringResource(id = R.string.account_number), + description = loanWithAssociations.accountNo ?: "", + descriptionStyle = MaterialTheme.typography.bodyLarge, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + DetailField( + title = stringResource(id = R.string.loan_type), + description = loanWithAssociations.loanType?.value ?: "", + descriptionStyle = MaterialTheme.typography.bodyLarge, + ) + Spacer(modifier = Modifier.height(8.dp)) + + DetailField( + title = stringResource(id = R.string.currency), + description = loanWithAssociations.summary?.currency?.code ?: "", + descriptionStyle = MaterialTheme.typography.bodyLarge, + ) + + if (isActive) { + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { + Button(onClick = { makePayment.invoke() }) { + Text(text = stringResource(id = R.string.make_payment)) + } + } + } + } + } +} + +@Composable +fun LoanMonitorComponent( + modifier: Modifier = Modifier, + viewLoanSummary: () -> Unit, + viewCharges: () -> Unit, + viewRepaymentSchedule: () -> Unit, + viewTransactions: () -> Unit, + viewQr: () -> Unit +) { + Column(modifier = modifier.fillMaxWidth()) { + Text( + text = stringResource(id = R.string.monitor), + style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold), + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(modifier = Modifier.height(8.dp)) + + MonitorListItemWithIcon( + titleId = R.string.loan_summary, + subTitleId = R.string.view_loan_summary, + iconId = R.drawable.ic_surveys_48px, + onClick = { viewLoanSummary.invoke() } + ) + + MonitorListItemWithIcon( + titleId = R.string.loan_charges, + subTitleId = R.string.view_charges, + iconId = R.drawable.ic_charges, + onClick = { viewCharges.invoke() } + ) + + MonitorListItemWithIcon( + titleId = R.string.repayment_schedule, + subTitleId = R.string.view_repayment, + iconId = R.drawable.ic_charges, + onClick = { viewRepaymentSchedule.invoke() } + ) + + MonitorListItemWithIcon( + titleId = R.string.transactions, + subTitleId = R.string.view_transactions, + iconId = R.drawable.ic_compare_arrows_black_24dp, + onClick = { viewTransactions.invoke() } + ) + + MonitorListItemWithIcon( + titleId = R.string.qr_code, + subTitleId = R.string.view_qr_code, + iconId = R.drawable.ic_qrcode_scan, + onClick = { viewQr.invoke() } + ) + } +} + +@Composable +private fun getNextInstallment( + loanWithAssociations: LoanWithAssociations, + currencySymbol: String +): String { + loanWithAssociations.repaymentSchedule?.periods?.forEach { period -> + val dueDate = period.dueDate + if (dueDate == loanWithAssociations.summary?.getOverdueSinceDate()) { + return stringResource( + id = R.string.string_and_string, + currencySymbol, + CurrencyUtil.formatCurrency( + context = LocalContext.current, + amt = period.totalDueForPeriod ?: 0.0 + ) + ) + } + } + return stringResource(id = R.string.not_available) +} + +@Preview(showSystemUi = true) +@Composable +fun LoanAccountDetailContentPreview() { + MifosMobileTheme { + LoanAccountDetailContent( + loanWithAssociations = LoanWithAssociations(), + {}, {}, {}, {}, {}, {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailScreen.kt b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailScreen.kt new file mode 100644 index 000000000..8376ef0e3 --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailScreen.kt @@ -0,0 +1,117 @@ +package org.mifos.mobile.ui.loan_account + +import android.widget.Toast +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import org.mifos.mobile.R +import org.mifos.mobile.core.ui.component.EmptyDataView +import org.mifos.mobile.core.ui.component.MifosProgressIndicator +import org.mifos.mobile.core.ui.component.NoInternet +import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.models.accounts.loan.LoanWithAssociations +import org.mifos.mobile.utils.Network + +@Composable +fun LoanAccountDetailScreen( + uiState: LoanAccountDetailUiState, + navigateBack: () -> Unit, + viewGuarantor: () -> Unit, + updateLoan: () -> Unit, + withdrawLoan: () -> Unit, + retryConnection: () -> Unit, + viewLoanSummary: () -> Unit, + viewCharges: () -> Unit, + viewRepaymentSchedule: () -> Unit, + viewTransactions: () -> Unit, + viewQr: () -> Unit, + makePayment: () -> Unit +) { + Column { + LoanAccountDetailTopBar( + navigateBack = navigateBack, + viewGuarantor = viewGuarantor, + updateLoan = updateLoan, + withdrawLoan = withdrawLoan + ) + + when (uiState) { + is LoanAccountDetailUiState.Success -> { + LoanAccountDetailContent( + loanWithAssociations = uiState.loanWithAssociations, + viewLoanSummary = viewLoanSummary, + viewCharges = viewCharges, + viewRepaymentSchedule = viewRepaymentSchedule, + viewTransactions = viewTransactions, + viewQr = viewQr, + makePayment = makePayment + ) + } + + is LoanAccountDetailUiState.Loading -> { + MifosProgressIndicator(modifier = Modifier.fillMaxSize()) + } + + is LoanAccountDetailUiState.Error -> { + ErrorComponent(retryConnection = retryConnection) + } + + is LoanAccountDetailUiState.ApprovalPending -> { + EmptyDataView( + modifier = Modifier.fillMaxSize(), + icon = R.drawable.ic_assignment_turned_in_black_24dp, + error = R.string.approval_pending + ) + } + + is LoanAccountDetailUiState.WaitingForDisburse -> { + EmptyDataView( + modifier = Modifier.fillMaxSize(), + icon = R.drawable.ic_assignment_turned_in_black_24dp, + error = R.string.waiting_for_disburse + ) + } + } + + } +} + +@Composable +fun ErrorComponent( + retryConnection: () -> Unit +) { + val context = LocalContext.current + if (!Network.isConnected(context)) { + NoInternet( + icon = R.drawable.ic_portable_wifi_off_black_24dp, + error = R.string.no_internet_connection, + isRetryEnabled = true, + retry = retryConnection + ) + Toast.makeText( + context, + stringResource(R.string.internet_not_connected), + Toast.LENGTH_SHORT, + ).show() + } else { + EmptyDataView( + icon = R.drawable.ic_error_black_24dp, + error = R.string.loan_account_details + ) + } +} + +@Composable +@Preview(showSystemUi = true) +fun LoanAccountDetailScreenPreview() { + MifosMobileTheme { + LoanAccountDetailScreen( + uiState = LoanAccountDetailUiState.Success(LoanWithAssociations()), + {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailTopBar.kt b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailTopBar.kt new file mode 100644 index 000000000..71d548483 --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountDetailTopBar.kt @@ -0,0 +1,99 @@ +package org.mifos.mobile.ui.loan_account + +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.mifos.mobile.R +import org.mifos.mobile.core.ui.theme.MifosMobileTheme + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun LoanAccountDetailTopBar( + navigateBack: () -> Unit, + viewGuarantor: () -> Unit, + updateLoan: () -> Unit, + withdrawLoan: () -> Unit +) { + var showMenu by remember { mutableStateOf(false) } + + TopAppBar( + modifier = Modifier, + title = { Text(text = stringResource(id = R.string.loan_account_details)) }, + navigationIcon = { + IconButton( + onClick = { navigateBack.invoke() } + ) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back Arrow", + tint = MaterialTheme.colorScheme.onSurface + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.background + ), + actions = { + IconButton(onClick = { showMenu = !showMenu }) { + Icon( + imageVector = Icons.Filled.MoreVert, + contentDescription = "Menu", + tint = MaterialTheme.colorScheme.onSurface + ) + } + + DropdownMenu( + expanded = showMenu, + modifier = Modifier.padding(start = 16.dp, end = 32.dp), + onDismissRequest = { showMenu = false } + ) { + DropdownMenuItem( + text = { + Text(text = stringResource(id = R.string.view_guarantor)) + }, + onClick = { viewGuarantor.invoke() } + ) + DropdownMenuItem( + text = { + Text(text = stringResource(id = R.string.update_loan)) + }, + onClick = { updateLoan.invoke() } + ) + DropdownMenuItem( + text = { + Text(text = stringResource(id = R.string.withdraw_loan)) + }, + onClick = { withdrawLoan.invoke() } + ) + } + } + ) +} + +@Preview +@Composable +fun LoanAccountDetailTopBarPreview() { + MifosMobileTheme { + LoanAccountDetailTopBar({},{},{},{}) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailFragment.kt b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailFragment.kt new file mode 100644 index 000000000..4f694b804 --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailFragment.kt @@ -0,0 +1,227 @@ +package org.mifos.mobile.ui.loan_account + +import android.os.Bundle +import android.view.* +import android.widget.Toast +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch +import org.mifos.mobile.R +import org.mifos.mobile.api.local.PreferencesHelper +import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.models.accounts.loan.LoanWithAssociations +import org.mifos.mobile.ui.activities.LoanAccountContainerActivity +import org.mifos.mobile.ui.activities.SavingsAccountContainerActivity +import org.mifos.mobile.ui.activities.base.BaseActivity +import org.mifos.mobile.ui.enums.AccountType +import org.mifos.mobile.ui.enums.ChargeType +import org.mifos.mobile.ui.enums.LoanState +import org.mifos.mobile.ui.fragments.ClientChargeFragment +import org.mifos.mobile.ui.fragments.GuarantorListFragment +import org.mifos.mobile.ui.fragments.LoanAccountSummaryFragment +import org.mifos.mobile.ui.fragments.LoanAccountTransactionFragment +import org.mifos.mobile.ui.fragments.LoanAccountWithdrawFragment +import org.mifos.mobile.ui.fragments.LoanApplicationFragment +import org.mifos.mobile.ui.fragments.LoanRepaymentScheduleFragment +import org.mifos.mobile.ui.fragments.QrCodeDisplayFragment +import org.mifos.mobile.ui.fragments.SavingsMakeTransferFragment +import org.mifos.mobile.ui.fragments.base.BaseFragment +import org.mifos.mobile.ui.savings_account.SavingsAccountDetailScreen +import org.mifos.mobile.utils.* +import org.mifos.mobile.utils.ParcelableAndSerializableUtils.getCheckedParcelable +import javax.inject.Inject + +/* +~This project is licensed under the open source MPL V2. +~See https://github.com/openMF/self-service-app/blob/master/LICENSE.md +*/ /** + * @author Vishwajeet + * @since 19/08/16 + */ +@AndroidEntryPoint +class LoanAccountsDetailFragment : BaseFragment() { + @Inject + lateinit var preferencesHelper: PreferencesHelper + private val viewModel: LoanAccountsDetailViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (arguments != null) { + viewModel.setLoanId(arguments?.getLong(Constants.LOAN_ID)) + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + viewModel.loadLoanAccountDetails(viewModel.loanId) + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + MifosMobileTheme { + LoanAccountDetailScreen( + uiState = viewModel.loanUiState.value, + navigateBack = { activity?.finish() }, + viewGuarantor = { viewGuarantor() }, + updateLoan = { updateLoan() }, + withdrawLoan = { withdrawLoan() }, + viewLoanSummary = { onLoanSummaryClicked() }, + viewCharges = { chargesClicked() }, + viewRepaymentSchedule = { onRepaymentScheduleClicked() }, + viewTransactions = { onTransactionsClicked() }, + viewQr = { onQrCodeClicked() }, + makePayment = { onMakePaymentClicked() }, + retryConnection = { retryClicked() } + ) + } + } + } + } + + override fun onResume() { + super.onResume() + (activity as? BaseActivity)?.hideToolbar() + } + + /** + * Opens [SavingsMakeTransferFragment] to Make a payment for loan account with given + * `loanId` + */ + private fun onMakePaymentClicked() { + (activity as BaseActivity?)?.replaceFragment( + SavingsMakeTransferFragment.newInstance( + viewModel.loanId, + viewModel.loanWithAssociations?.summary?.totalOutstanding, + Constants.TRANSFER_PAY_TO, + ), + true, + R.id.container, + ) + } + + /** + * Opens [LoanAccountSummaryFragment] + */ + private fun onLoanSummaryClicked() { + (activity as BaseActivity?)?.replaceFragment( + LoanAccountSummaryFragment.newInstance( + viewModel.loanWithAssociations, + ), + true, + R.id.container, + ) + } + + /** + * Opens [LoanRepaymentScheduleFragment] + */ + private fun onRepaymentScheduleClicked() { + (activity as BaseActivity?)?.replaceFragment( + LoanRepaymentScheduleFragment.newInstance( + viewModel.loanId, + ), + true, + R.id.container, + ) + } + + /** + * Opens [LoanAccountTransactionFragment] + */ + private fun onTransactionsClicked() { + (activity as BaseActivity?)?.replaceFragment( + LoanAccountTransactionFragment.newInstance( + viewModel.loanId, + ), + true, + R.id.container, + ) + } + + private fun chargesClicked() { + (activity as BaseActivity?)?.replaceFragment( + ClientChargeFragment.newInstance( + viewModel.loanWithAssociations?.id?.toLong(), + ChargeType.LOAN, + ), + true, + R.id.container, + ) + } + + private fun onQrCodeClicked() { + val accountDetailsInJson = QrCodeGenerator.getAccountDetailsInString( + viewModel.loanWithAssociations?.accountNo, + preferencesHelper.officeName, + AccountType.LOAN, + ) + (activity as BaseActivity?)?.replaceFragment( + QrCodeDisplayFragment.newInstance( + accountDetailsInJson, + ), + true, + R.id.container, + ) + } + + private fun retryClicked() { + if (Network.isConnected(context)) { + viewModel.loadLoanAccountDetails(viewModel.loanId) + } else { + Toast.makeText( + context, + getString(R.string.internet_not_connected), + Toast.LENGTH_SHORT, + ).show() + } + } + + private fun updateLoan() { + (activity as BaseActivity?)?.replaceFragment( + LoanApplicationFragment.newInstance( + LoanState.UPDATE, + viewModel.loanWithAssociations, + ), + true, + R.id.container, + ) + } + + private fun withdrawLoan() { + (activity as BaseActivity?)?.replaceFragment( + LoanAccountWithdrawFragment.newInstance( + viewModel.loanWithAssociations, + ), + true, + R.id.container, + ) + } + + private fun viewGuarantor() { + (activity as BaseActivity?)?.replaceFragment( + GuarantorListFragment.newInstance( + viewModel.loanId, + ), + true, + R.id.container, + ) + } + + companion object { + @JvmStatic + fun newInstance(loanId: Long): LoanAccountsDetailFragment { + val fragment = LoanAccountsDetailFragment() + val args = Bundle() + args.putLong(Constants.LOAN_ID, loanId) + fragment.arguments = args + return fragment + } + } +} diff --git a/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailViewModel.kt b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailViewModel.kt new file mode 100644 index 000000000..1bdad9f9a --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailViewModel.kt @@ -0,0 +1,61 @@ +package org.mifos.mobile.ui.loan_account + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.launch +import org.mifos.mobile.models.accounts.loan.LoanWithAssociations +import org.mifos.mobile.repositories.LoanRepository +import org.mifos.mobile.utils.Constants +import javax.inject.Inject + +@HiltViewModel +class LoanAccountsDetailViewModel @Inject constructor(private val loanRepositoryImp: LoanRepository) : + ViewModel() { + + private val _loanUiState = + mutableStateOf(LoanAccountDetailUiState.Loading) + val loanUiState: State get() = _loanUiState + + private var _loanId: Long? = 0 + val loanId: Long? get() = _loanId + + private var _loanWithAssociations: LoanWithAssociations? = null + val loanWithAssociations get() = _loanWithAssociations + + fun loadLoanAccountDetails(loanId: Long?) { + viewModelScope.launch { + _loanUiState.value = LoanAccountDetailUiState.Loading + loanRepositoryImp.getLoanWithAssociations(Constants.REPAYMENT_SCHEDULE, loanId) + ?.catch { _loanUiState.value = LoanAccountDetailUiState.Error } + ?.collect { processLoanDetailsResponse(it) } + } + } + + private fun processLoanDetailsResponse(loanWithAssociations: LoanWithAssociations?) { + _loanWithAssociations = loanWithAssociations + val uiState = when { + loanWithAssociations == null -> LoanAccountDetailUiState.Error + loanWithAssociations.status?.active == true -> LoanAccountDetailUiState.Success(loanWithAssociations) + loanWithAssociations.status?.pendingApproval == true -> LoanAccountDetailUiState.ApprovalPending + loanWithAssociations.status?.waitingForDisbursal == true -> LoanAccountDetailUiState.WaitingForDisburse + else -> LoanAccountDetailUiState.Success(loanWithAssociations) + } + _loanUiState.value = uiState + } + + fun setLoanId(id: Long?) { + _loanId = id + } +} + +sealed class LoanAccountDetailUiState { + data object Loading : LoanAccountDetailUiState() + data object Error : LoanAccountDetailUiState() + data object ApprovalPending : LoanAccountDetailUiState() + data object WaitingForDisburse : LoanAccountDetailUiState() + data class Success(val loanWithAssociations: LoanWithAssociations) : LoanAccountDetailUiState() +} diff --git a/app/src/main/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModel.kt b/app/src/main/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModel.kt deleted file mode 100644 index ba5c8a0a8..000000000 --- a/app/src/main/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModel.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.mifos.mobile.viewModels - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.launch -import org.mifos.mobile.R -import org.mifos.mobile.repositories.LoanRepository -import org.mifos.mobile.utils.Constants -import org.mifos.mobile.utils.LoanUiState -import javax.inject.Inject - -@HiltViewModel -class LoanAccountsDetailViewModel @Inject constructor(private val loanRepositoryImp: LoanRepository) : - ViewModel() { - - private val _loanUiState = MutableStateFlow(LoanUiState.Loading) - val loanUiState: StateFlow get() = _loanUiState - - fun loadLoanAccountDetails(loanId: Long?) { - viewModelScope.launch { - _loanUiState.value = LoanUiState.Loading - loanRepositoryImp.getLoanWithAssociations( - Constants.REPAYMENT_SCHEDULE, - loanId, - )?.catch { - _loanUiState.value = - LoanUiState.ShowError(R.string.loan_account_details) - }?.collect { - _loanUiState.value = it?.let { it1 -> LoanUiState.ShowLoan(it1) }!! - } - } - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_loan_account_details.xml b/app/src/main/res/layout/fragment_loan_account_details.xml index 84284a6ed..5a31d36e5 100644 --- a/app/src/main/res/layout/fragment_loan_account_details.xml +++ b/app/src/main/res/layout/fragment_loan_account_details.xml @@ -16,7 +16,7 @@ android:layout_marginHorizontal="@dimen/Mifos.DesignSystem.Spacing.screenHorizontalMargin" android:orientation="vertical" android:paddingVertical="@dimen/Mifos.DesignSystem.Spacing.screenVerticalMargin" - android:visibility="gone"> + >