diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/category/create/CategoryCreateViewModel.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/category/create/CategoryCreateViewModel.kt index 33cc642..c7e7047 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/category/create/CategoryCreateViewModel.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/category/create/CategoryCreateViewModel.kt @@ -66,39 +66,34 @@ class CategoryCreateViewModel : CommonViewModel(S } } private fun selectProduct(catalogProduct: CatalogProduct) { - if (!state.addedProducts.contains(catalogProduct)) { - update { - copy( - addedProducts = addedProducts + catalogProduct, - selectedProduct = catalogProduct, - items = items.filterNot { it.item == catalogProduct }, - addedItems = addedItems + catalogProduct.mapToCategoryUiItems( - isSelected = true, - onDeleteClicked = {removeProduct(it)}, - onSelectClicked = { _, _ -> } - ), - categoryProducts = categoryProducts + - CategoryProduct( - id = requireNotNull(catalogProduct.product.id), - displayOrder = state.categoryProducts.size + 1 - ) + if (state.addedProducts.contains(catalogProduct)){ return } + update { copy( + addedProducts = addedProducts + catalogProduct, + selectedProduct = catalogProduct, + items = items.filterNot { it.item == catalogProduct }, + addedItems = addedItems + catalogProduct.mapToCategoryUiItems( + isSelected = true, + onDeleteClicked = {removeProduct(it)}, + onSelectClicked = { _, _ -> } + ), + categoryProducts = categoryProducts + + CategoryProduct( + id = requireNotNull(catalogProduct.product.id), + displayOrder = state.categoryProducts.size + 1 ) - } - } + ) } } private fun removeProduct(catalogProduct: CatalogProduct) { - update { - copy( - addedProducts = addedProducts - catalogProduct, - categoryProducts = categoryProducts.filterNot { it.id == catalogProduct.product.id }, - addedItems = addedItems.filterNot {it.item == catalogProduct }, - items = listOf(catalogProduct.mapToCategoryUiItems( - isSelected = false, - onDeleteClicked = { removeProduct(it) }, - onSelectClicked = { _, _ -> selectProduct(catalogProduct) } - )) + items - ) - } + update { copy( + addedProducts = addedProducts - catalogProduct, + categoryProducts = categoryProducts.filterNot { it.id == catalogProduct.product.id }, + addedItems = addedItems.filterNot {it.item == catalogProduct }, + items = listOf(catalogProduct.mapToCategoryUiItems( + isSelected = false, + onDeleteClicked = { removeProduct(it) }, + onSelectClicked = { _, _ -> selectProduct(catalogProduct) } + )) + items + ) } } fun create() { diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/category/update/CategoryUpdateFragment.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/category/update/CategoryUpdateFragment.kt index 2ebd4a1..c36c164 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/category/update/CategoryUpdateFragment.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/category/update/CategoryUpdateFragment.kt @@ -6,14 +6,12 @@ import android.widget.Toast import androidx.fragment.app.viewModels import com.godaddy.commerce.sdk.util.isNotNullOrBlank import com.godaddy.commerce.services.sample.R -import com.godaddy.commerce.services.sample.catalog.category.create.CategoryCreateViewModel import com.godaddy.commerce.services.sample.common.extensions.bindTo import com.godaddy.commerce.services.sample.common.extensions.launch import com.godaddy.commerce.services.sample.common.extensions.observableField import com.godaddy.commerce.services.sample.common.view.CommonFragment import com.godaddy.commerce.services.sample.common.view.bindOnCommonViewModelUpdates import com.godaddy.commerce.services.sample.databinding.CategoryUpdateFragmentBinding -import timber.log.Timber class CategoryUpdateFragment : CommonFragment(R.layout.category_update_fragment) { @@ -35,7 +33,7 @@ class CategoryUpdateFragment : ) val allItems by observableField( stateFlow = { viewModel.stateFlow }, - map = { addedItems + items} + map = { addedItems + items } ) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -49,8 +47,8 @@ class CategoryUpdateFragment : private fun bindToAddedItemsEvents() { launch { viewModel.stateFlow.bindTo( CategoryUpdateViewModel.State::addedItems) { - dataBinding.ProductRecyclerView.scrollToPosition(0) - } + dataBinding.ProductRecyclerView.scrollToPosition(0) + } } } @@ -60,7 +58,6 @@ class CategoryUpdateFragment : CategoryUpdateViewModel.State::updatedCategoryId ) { id -> if (id.isNotNullOrBlank()) { - Timber.tag("test10").d("no") return@bindTo Toast.makeText( requireContext(), "Category with id [${id}] was updated", diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxFragment.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxFragment.kt index 300ccb5..4d34cb3 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxFragment.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxFragment.kt @@ -13,7 +13,6 @@ import timber.log.Timber class TaxFragment : CommonFragment(R.layout.tax_fragment) { - private val viewModel: TaxViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxRecyclerItem.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxRecyclerItem.kt index f230552..efb4a7f 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxRecyclerItem.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxRecyclerItem.kt @@ -7,39 +7,31 @@ import androidx.navigation.findNavController import com.godaddy.commerce.catalog.model.CatalogTax import com.godaddy.commerce.services.sample.R import com.godaddy.commerce.services.sample.common.binding.RecyclerAdapterItem -import com.godaddy.commerce.services.sample.databinding.TaxAssociationItemBinding import com.godaddy.commerce.services.sample.databinding.TaxItemBinding -import com.godaddy.commerce.taxes.models.AssociationItems -import java.util.* data class TaxRecyclerItem( override val item: CatalogTax, - override val onBinding: (binding: TaxItemBinding, position: Int, getItem: () -> CatalogTax) -> Unit = { _, _, _ -> }, -) : RecyclerAdapterItem(item, onBinding) + val amountString: String = if (item.tax.amount != null) ( + item.tax.amount?.amount?.value.toString() + ' ' + item.tax.amount?.amount?.currencyCode + ) else "N/A", + val percentageString: String = if (item.tax.amount == null) ( + item.tax.percentage?.percentage.toString() + '%') + else "N/A", + + val classificationCount: String = item.tax.classifications.orEmpty().size.toString(), + val overrideCount: String = item.tax.overrides.orEmpty().size.toString(), + + override val onBinding: (binding: TaxItemBinding, position: Int, getItem: () -> CatalogTax) -> Unit = { _, _, _ -> } + + ) : RecyclerAdapterItem(item, onBinding) inline fun CatalogTax.mapToUiItems(): TaxRecyclerItem { return TaxRecyclerItem(this) { binding, _, getItem -> binding.updateBt.setOnClickListener { it.findNavController().navigate( resId = R.id.taxUpdateFragment, - args = bundleOf("id" to getItem().tax.id?.toString()) + args = bundleOf("id" to getItem().tax.id) ) } } } - -data class TaxAssociationRecyclerItem( - override val item: AssociationItems, - override val onBinding: (binding: TaxAssociationItemBinding, position: Int, getItem: () -> AssociationItems) -> Unit = { _, _, _ -> } -) : RecyclerAdapterItem(item, onBinding){ - override fun getChangePayload(other: RecyclerAdapterItem): Any = Unit -} - - -inline fun AssociationItems.mapToUiItems(crossinline deleteItemId: (UUID) -> Unit): TaxAssociationRecyclerItem { - return TaxAssociationRecyclerItem(this) { binding, _, getItem -> - binding.deleteBtn.setOnClickListener { - getItem().id?.let(deleteItemId) - } - } -} \ No newline at end of file diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxViewModel.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxViewModel.kt index 451fb44..1f9d129 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxViewModel.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/TaxViewModel.kt @@ -2,16 +2,15 @@ package com.godaddy.commerce.services.sample.catalog.tax -import android.os.Bundle +import android.util.Log import androidx.lifecycle.viewModelScope import com.godaddy.commerce.catalog.CatalogIntents -import com.godaddy.commerce.catalog.TaxParams -import com.godaddy.commerce.catalog.model.CatalogTaxes +import com.godaddy.commerce.catalog.model.CatalogTax import com.godaddy.commerce.common.DataSource import com.godaddy.commerce.common.FilterBy import com.godaddy.commerce.provider.catalog.CatalogContract -import com.godaddy.commerce.services.sample.catalog.onSuccess -import com.godaddy.commerce.services.sample.common.extensions.onError +import com.godaddy.commerce.sdk.catalog.TaxParamsExt +import com.godaddy.commerce.sdk.catalog.getCatalogTaxes import com.godaddy.commerce.services.sample.common.extensions.subscribeOnUpdates import com.godaddy.commerce.services.sample.common.viewmodel.CommonState import com.godaddy.commerce.services.sample.common.viewmodel.CommonViewModel @@ -20,7 +19,7 @@ import com.godaddy.commerce.services.sample.di.CommerceDependencyProvider import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.suspendCancellableCoroutine +import timber.log.Timber class TaxViewModel : CommonViewModel(State()) { @@ -28,8 +27,6 @@ class TaxViewModel : CommonViewModel(State()) { init { loadTaxes() - - // CS sends an event when data was changed. Subscribe on it and refresh the list. CommerceDependencyProvider.getContext().subscribeOnUpdates( CatalogIntents.ACTION_TAXES_CHANGED ).onEach { @@ -40,26 +37,19 @@ class TaxViewModel : CommonViewModel(State()) { fun loadTaxes() { execute { val service = serviceClient.getService().getOrThrow() - val bundle = Bundle().apply { - // data source defines data provider: local db, remote or remote only if there are no data in local db. - // It is better to use REMOTE_IF_EMPTY in most cases to improve UX and performance. - putParcelable(TaxParams.DATA_SOURCE, DataSource.REMOTE_IF_EMPTY) - - // Add pagination to improve UX and avoid TooLargeTransactionException - putInt(TaxParams.PAGE_OFFSET, 0) - putInt(TaxParams.PAGE_SIZE, 100) - - // sorting is optional. List can be sorted by any column in database. - putString(TaxParams.SORT_BY, CatalogContract.Tax.Columns.UPDATED_AT) - - // filter is optional. In this example we do showing taxes which are enabled. - putParcelable(TaxParams.FILTER_BY, FilterBy(CatalogContract.Tax.Columns.STATUS, "1", FilterBy.ComparisonOperator.EQUAL)) - } - val response = suspendCancellableCoroutine { - service.getCatalogTaxes(bundle, it.onSuccess(), it.onError()) - } + val bundle = TaxParamsExt.toBundle( + dataSource = DataSource.REMOTE_IF_EMPTY, + pageOffset = 0, + pageSize = 100, + sortBy = CatalogContract.Tax.Columns.UPDATED_AT, + includeClassification = true, + includeOverrides = true, + + ) + val response = service.getCatalogTaxes(bundle) update { copy(items = response?.taxes.orEmpty().map { it.mapToUiItems() }) } } + } data class State( diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/create/TaxCreateFragment.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/create/TaxCreateFragment.kt index 73dbd6f..ddcad6b 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/create/TaxCreateFragment.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/create/TaxCreateFragment.kt @@ -1,6 +1,7 @@ package com.godaddy.commerce.services.sample.catalog.tax.create import android.os.Bundle +import android.util.Log import android.view.View import android.widget.Toast import androidx.fragment.app.viewModels @@ -13,6 +14,9 @@ import com.godaddy.commerce.services.sample.common.extensions.observableField import com.godaddy.commerce.services.sample.common.view.CommonFragment import com.godaddy.commerce.services.sample.common.view.bindOnCommonViewModelUpdates import com.godaddy.commerce.services.sample.databinding.TaxCreateFragmentBinding +import com.godaddy.commerce.services.sample.catalog.tax.create.TaxCreateViewModel.DialogType +import com.godaddy.commerce.services.sample.catalog.tax.update.TaxUpdateViewModel +import timber.log.Timber class TaxCreateFragment : CommonFragment(R.layout.tax_create_fragment) { @@ -23,21 +27,23 @@ class TaxCreateFragment : stateFlow = { viewModel.stateFlow }, map = TaxCreateViewModel.State::createTaxOverride ) - - val types by observableField( + val showTaxClassification by observableField( + stateFlow = { viewModel.stateFlow }, + map = TaxCreateViewModel.State::createTaxClassification + ) + val taxTypes by observableField( stateFlow = { viewModel.stateFlow }, - map = TaxCreateViewModel.State::label + map = TaxCreateViewModel.State::types + ) + val taxTypePos by observableField( + stateFlow = { viewModel.stateFlow }, + map = { taxType.let{ types.indexOf(it)} } ) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) bindOnCommonViewModelUpdates(viewModel) - launch { - viewModel.stateFlow.bindTo( - map = { products to showProductDialog }, - update = ::showProductsDialog - ) - } + bindToTaxDialogEvents() launch { viewModel.stateFlow.bindTo(TaxCreateViewModel.State::createdId) { id -> id ?: return@bindTo @@ -49,15 +55,24 @@ class TaxCreateFragment : } } } + private fun bindToTaxDialogEvents(){ + launch { viewModel.stateFlow.bindTo( + keySelector = { it.dialogType }, + map = { this }, + update = { + if (it.dialogType != null && it.dialogType != DialogType.NO_SHOW){ + handleProductDialog(it.dialogList, it.dialogType) + } - - private fun showProductsDialog(pair: Pair, Boolean>) { - if (pair.second.not()) return + } + ) } + } + private fun handleProductDialog(products: List, dialogType: DialogType) { requireContext().dialogBuilder( "Select Product", - items = pair.first, + extras = dialogType, + items = products, map = { it.product.label }, - onSelected = viewModel::selectProduct - ).setOnDismissListener { viewModel.hideProductsDialog() }.create().show() - } -} \ No newline at end of file + onSelected = viewModel::handleProduct + ).setOnDismissListener { viewModel.hideDialog() }.create().show()} +} diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/create/TaxCreateViewModel.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/create/TaxCreateViewModel.kt index ba10438..14b210e 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/create/TaxCreateViewModel.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/create/TaxCreateViewModel.kt @@ -2,26 +2,18 @@ package com.godaddy.commerce.services.sample.catalog.tax.create -import android.os.Bundle -import androidx.core.os.bundleOf import androidx.lifecycle.viewModelScope -import co.poynt.api.model.Business -import com.godaddy.commerce.catalog.ProductParams -import com.godaddy.commerce.catalog.TaxConstants import com.godaddy.commerce.catalog.model.CatalogProduct -import com.godaddy.commerce.catalog.model.CatalogProducts import com.godaddy.commerce.catalog.model.CatalogTax -import com.godaddy.commerce.catalog.models.* import com.godaddy.commerce.common.DataSource -import com.godaddy.commerce.sdk.business.BusinessParams -import com.godaddy.commerce.services.sample.catalog.onSuccess -import com.godaddy.commerce.services.sample.common.extensions.onError -import com.godaddy.commerce.services.sample.common.extensions.onSuccess -import com.godaddy.commerce.services.sample.common.extensions.toSimpleMoney +import com.godaddy.commerce.provider.catalog.CatalogContract +import com.godaddy.commerce.sdk.catalog.ProductParamsExt +import com.godaddy.commerce.sdk.catalog.getCatalogProducts +import com.godaddy.commerce.sdk.catalog.postCatalogTax +import com.godaddy.commerce.services.sample.common.util.DEFAULT_CURRENCY_CODE import com.godaddy.commerce.services.sample.common.viewmodel.CommonState import com.godaddy.commerce.services.sample.common.viewmodel.CommonViewModel import com.godaddy.commerce.services.sample.common.viewmodel.ToolbarState -import com.godaddy.commerce.services.sample.di.CommerceDependencyProvider import com.godaddy.commerce.services.sample.di.CommerceDependencyProvider.getCatalogService import com.godaddy.commercecore.models.Amount import com.godaddy.commercecore.models.Classification @@ -31,189 +23,203 @@ import com.godaddy.commercecore.models.OverrideRate import com.godaddy.commercecore.models.Percentage import com.godaddy.commercecore.models.Tax import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.suspendCancellableCoroutine -import java.util.* class TaxCreateViewModel : CommonViewModel(State()) { private val catalogServiceClient = getCatalogService(viewModelScope) + init { + loadProducts() + } fun onLabelChanged(value: String) { update { copy(label = value) } } - fun onRatePercentageChanged(value: String) { - updatePercentage( Percentage(value)) + update { copy(percentage = Percentage(value, value)) } } - fun onAmountChanged(value: String) { - updateAmount( Amount(Money(value)) ) + value.toLongOrNull()?.let { + val amount = Money(DEFAULT_CURRENCY_CODE, it) + update { + copy(amount = Amount(amount, amount)) + } + } } - - fun onTaxOverrideRateAmountChanged(value: String) { - updateTaxOverrideRate { copy(amount = Amount(amount = Money(value = value.toLongOrNull()))) } + fun onTaxOverrideLabelChanged(value: String) { + update { copy(overrideLabel = value) } } - - fun onTaxOverrideRatePercentageChanged(value: String) { - updateTaxOverrideRate { copy(ratePercentage = Percentage(value)) } + fun onTaxOverrideCustomRateChanged(value: String) { + update { + copy(customRate = OverrideRate(ratePercentage = Percentage(value))) + } } - - fun showProductDialog() { + fun onTaxClassificationLabelChanged(value: String) { + update { copy(classificationLabel = value) } + } + fun onTaxTypeChange(position: Int) { + update { copy( + taxType = state.types[position] + ) } + } + private fun loadProducts(query: String? = null) { execute { - if (state.products.isEmpty()) { - val params = bundleOf( - // recommended data source is REMOTE_IF_EMPTY. - ProductParams.DATA_SOURCE to DataSource.REMOTE_IF_EMPTY, - // pagination is required otherwise exception can be thrown. - ProductParams.PAGE_SIZE to 100, - ProductParams.PAGE_OFFSET to 0, - ) - val service = catalogServiceClient.getService().getOrThrow() - val response = suspendCancellableCoroutine { - service.getCatalogProducts(params, it.onSuccess(), it.onError()) - } - update { - copy( - products = response?.products.orEmpty(), - showProductDialog = true - ) - } - } else { - update { copy(showProductDialog = true) } - } + val service = catalogServiceClient.getService().getOrThrow() + val bundle = ProductParamsExt.toBundle( + dataSource = DataSource.REMOTE_IF_EMPTY, + pageOffset = DEFAULT_TAX_PRODUCTS_PAGE_OFFSET, + pageSize = DEFAULT_TAX_PRODUCTS_PAGE_SIZE, + sortBy = CatalogContract.Product.Columns.UPDATED_AT, + searchTerm = query, + ) + val response = service.getCatalogProducts(bundle) + val products = response?.products.orEmpty().toSet() + update { copy (allProducts = products) } } } - - fun hideProductsDialog() { - update { copy(showProductDialog = false) } - } - - fun switchTaxOverride(isShow: Boolean) { update { copy(createTaxOverride = isShow) } } - - fun selectProduct(product: CatalogProduct) { - update { copy(selectedProduct = product) } + fun switchTaxClassification(isShow: Boolean) { + update { copy(createTaxClassification = isShow) } } + fun hideDialog(){ + update{ copy(dialogType = DialogType.NO_SHOW) } + } + fun addClassificationProduct(){ + update { + val classificationProducts = allProducts.filter { + !classificationProductIds.contains(it.product.id) + } - fun create() { - execute { - val label = requireNotNull(state.label) { "Name is required" } - - val request = CatalogTax( - tax = Tax( - label = state.label, - - percentage = state.percentage, - amount = state.amount, - - - ) + copy( + dialogType = DialogType.ADD_CLASSIFICATION, + dialogList = classificationProducts ) - val catalogService = catalogServiceClient.getService().getOrThrow() - - // Step 1: create tax - val response = suspendCancellableCoroutine { - catalogService.postCatalogTax(request, Bundle.EMPTY, it.onSuccess(), it.onError()) + } + } + fun removeClassificationProduct(){ + update { + val classificationProducts = allProducts.filter { + classificationProductIds.contains(it.product.id) } - - - // Step 2: create tax association. Tax Association creates one-to-many relationship, one tax to many products/categories. - // !!! Does not allow to create store and products/categories association type. Only store or only products/categories. -// val items = associationItems() -// -// suspendCancellableCoroutine { -// val association = TaxAssociation( -// taxId = response?.id, -// name = "tax-association", -// items = items, -// type = TaxConstants.Association.TYPE_ASSOCIATION, -// ) -// catalogService.postPriceAdjustmentAssociation( -// association, -// Bundle.EMPTY, -// it.onSuccess(), -// it.onError() -// ) -// } - - // Step 3: We can create override rate for specific product/category. To do that need to create Tax Override Association. -// if (state.createTaxOverride) { -// requireNotNull(state.selectedProduct) { "Product is required to create override rate for the item" } -// suspendCancellableCoroutine { -// val overrideAssociation = TaxOverrideAssociation( -// taxId = response?.id, -// name = "tax-override", -// items = items, -// type = TaxConstants.Association.TYPE_ASSOCIATION_OVERRIDE, -// overrideValue = state.taxOverriderRate.toTaxOverriderRate() -// ) -// catalogService.postTaxOverrideAssociation( -// overrideAssociation, -// Bundle.EMPTY, -// it.onSuccess(), -// it.onError() -// ) -// } -// } - - update { copy(createdId = response?.tax?.id?.toString()) } + copy( + dialogType = DialogType.REMOVE_CLASSIFICATION, + dialogList = classificationProducts + ) } } - -// private suspend fun associationItems() = if (state.selectedProduct == null) { -// listOf( -// AssociationItems(type = TaxConstants.Association.TYPE_STORE, id = getStoreId()) -// ) -// } else { -// listOf( -// AssociationItems( -// type = TaxConstants.Association.TYPE_PRODUCT, -// id = state.selectedProduct?.productId -// ) -// ) -// } - - private suspend fun getStoreId(): UUID? { - val service = - CommerceDependencyProvider.getBusinessService(viewModelScope).getService().getOrThrow() - val result = suspendCancellableCoroutine { - service.getBusiness( - bundleOf(BusinessParams.FROM_CLOUD to false), - it.onSuccess(), - it.onError() + fun addOverrideProduct(){ + update { + val overrideProducts = allProducts.filter { + !overrideProductIds.contains(it.product.id) + } + copy( + dialogType = DialogType.ADD_OVERRIDE, + dialogList = overrideProducts ) } - return result.stores.firstOrNull()?.id } - - private fun updatePercentage(percentage: Percentage?) { - update { copy(percentage = percentage) } + fun removeOverrideProduct(){ + update { + val overrideProducts = allProducts.filter { + overrideProductIds.contains(it.product.id) + } + copy( + dialogType = DialogType.REMOVE_OVERRIDE, + dialogList = overrideProducts + ) + } } - - private fun updateAmount(amount: Amount?) { - update { copy(amount = amount) } + fun handleProduct(catalogProduct: CatalogProduct, dialogType: DialogType) { + val id = catalogProduct.product.id.toString() + if (dialogType == DialogType.ADD_CLASSIFICATION){ + update { copy (classificationProductIds = state.classificationProductIds.plus(id)) } + } + else if (state.dialogType == DialogType.ADD_OVERRIDE){ + update { copy (overrideProductIds = state.overrideProductIds.plus(id)) } + } + else if (dialogType == DialogType.REMOVE_CLASSIFICATION){ + update { copy (classificationProductIds = state.classificationProductIds.minus(id)) } + } + else if (dialogType == DialogType.REMOVE_OVERRIDE){ + update { copy (overrideProductIds = state.overrideProductIds.minus(id)) } + } } + fun create() { + execute { + val classifications = if (state.createTaxClassification) listOf( + Classification( + label = state.classificationLabel, + productIds = state.classificationProductIds + ) + ) else emptyList() + + val overrides = if (state.createTaxOverride) listOf( + Override( + label = state.overrideLabel, + customRate = state.customRate, + productIds = state.overrideProductIds + ) + ) else emptyList() + val percentage = if (state.taxType == "Percentage") state.percentage else null + val amount = if (state.taxType == "Amount") state.amount else null - private fun updateTaxOverrideRate(block: OverrideRate.() -> OverrideRate) { - update { copy(taxOverrideRate = taxOverrideRate?.let { block(it) }) } + val tax = Tax( + label = requireNotNull(state.label), + percentage = percentage, + amount = amount, + classifications = classifications, + overrides = overrides, + ) + val request = CatalogTax(tax = tax) + val catalogService = catalogServiceClient.getService().getOrThrow() + val response = catalogService.postCatalogTax(request) + update { copy(createdId = response?.tax?.id) } + } } data class State( override val commonState: CommonState = CommonState(), override val toolbarState: ToolbarState = ToolbarState(title = "Create Tax"), + // Tax Fields val label: String? = null, val amount: Amount? = null, val percentage: Percentage? = null, - val overrides: List? = null, - val selectedOverride: Override? = null, - val selectedProduct: CatalogProduct? = null, - val products: List = emptyList(), - val showProductDialog: Boolean = false, val createdId: String? = null, - val taxOverrideRate: OverrideRate? = null, + val types: List = listOf("Amount", "Percentage"), + val taxType: String = "Amount", + val selectedOverride: Override? = null, + val selectedClassification: Classification? = null, + + // New Override Fields + val overrideLabel: String? = null, + val customRate: OverrideRate? = null, val createTaxOverride: Boolean = false, - ) : ViewModelState + + // New Classification Fields + val classificationLabel: String? = null, + val createTaxClassification: Boolean = false, + + // Fields for handling product mapping + val classificationProductIds: Set = emptySet(), + val overrideProductIds: Set = emptySet(), + val dialogList: List = emptyList(), + val allProducts: Set = emptySet(), + val dialogType: DialogType? = null, + + ) : ViewModelState + + companion object{ + private const val DEFAULT_TAX_PRODUCTS_PAGE_SIZE = 100 + private const val DEFAULT_TAX_PRODUCTS_PAGE_OFFSET = 0 + } + enum class DialogType{ + ADD_CLASSIFICATION, + ADD_OVERRIDE, + REMOVE_CLASSIFICATION, + REMOVE_OVERRIDE, + NO_SHOW, + } + } \ No newline at end of file diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/TaxUpdateFragment.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/TaxUpdateFragment.kt index d06afd6..25baaf1 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/TaxUpdateFragment.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/TaxUpdateFragment.kt @@ -2,9 +2,15 @@ package com.godaddy.commerce.services.sample.catalog.tax.update import android.os.Bundle import android.view.View +import android.widget.Toast import androidx.fragment.app.viewModels +import com.godaddy.commerce.catalog.model.CatalogProduct +import com.godaddy.commerce.sdk.util.isNotNullOrBlank import com.godaddy.commerce.services.sample.R -import com.godaddy.commerce.services.sample.catalog.tax.update.TaxUpdateViewModel.State +import com.godaddy.commerce.services.sample.catalog.tax.update.TaxUpdateViewModel.DialogType +import com.godaddy.commerce.services.sample.common.extensions.bindTo +import com.godaddy.commerce.services.sample.common.extensions.dialogBuilder +import com.godaddy.commerce.services.sample.common.extensions.launch import com.godaddy.commerce.services.sample.common.extensions.observableField import com.godaddy.commerce.services.sample.common.view.CommonFragment import com.godaddy.commerce.services.sample.common.view.bindOnCommonViewModelUpdates @@ -12,29 +18,85 @@ import com.godaddy.commerce.services.sample.databinding.TaxUpdateFragmentBinding class TaxUpdateFragment : CommonFragment(R.layout.tax_update_fragment) { - - private val viewModel: TaxUpdateViewModel by viewModels() - val name by observableField({ viewModel.stateFlow }, { tax?.tax?.label }) -// val rateName by observableField({ viewModel.stateFlow }, { tax?.taxRates?.firstOrNull()?.name }) -// val amount by observableField( -// { viewModel.stateFlow }, -// { tax?.taxRates?.firstOrNull()?.amount?.value?.toString() }) -// val rate by observableField( -// { viewModel.stateFlow }, -// { tax?.taxRates?.firstOrNull()?.ratePercentage }) -// val selectedTypePos by observableField( -// { viewModel.stateFlow }, -// { tax?.taxRates?.firstOrNull()?.amountType?.let { amountTypes.indexOf(it) } }) - - val types by observableField( + val label by observableField( + stateFlow = { viewModel.stateFlow }, + map = TaxUpdateViewModel.State::label + ) + val amount by observableField( + stateFlow = {viewModel.stateFlow}, + map = { this.amount?.amount?.value?.toString() ?: "N/A"} + ) + val percentage by observableField( + stateFlow = {viewModel.stateFlow}, + map = {this.percentage?.percentage ?: "N/A"} + ) + val showTaxOverride by observableField( + stateFlow = { viewModel.stateFlow }, + map = TaxUpdateViewModel.State::updateTaxOverride + ) + val showTaxClassification by observableField( stateFlow = { viewModel.stateFlow }, - map = State::amountTypes + map = TaxUpdateViewModel.State::updateTaxClassification ) + val classificationLabel by observableField( + stateFlow = {viewModel.stateFlow}, + map = TaxUpdateViewModel.State::classificationLabel + ) + val overrideLabel by observableField( + stateFlow = {viewModel.stateFlow}, + map = TaxUpdateViewModel.State::overrideLabel + ) + val customRate by observableField( + stateFlow = {viewModel.stateFlow}, + map = {this.customRate?.ratePercentage?.percentage ?: "N/A"} + ) + val taxTypes by observableField({ viewModel.stateFlow }, TaxUpdateViewModel.State::types) + val taxTypePos by observableField({ viewModel.stateFlow }, { taxType.let{ types.indexOf(it)} }) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + dataBinding.fragment = this bindOnCommonViewModelUpdates(viewModel) + bindToTaxUpdatedEvents() + bindToTaxDialogEvents() + } + private fun bindToTaxDialogEvents(){ + launch { viewModel.stateFlow.bindTo( + keySelector = { it.dialogType }, + map = { this }, + update = { + if (it.dialogType != null && it.dialogType != DialogType.NO_SHOW){ + handleProductDialog(it.dialogList, it.dialogType) + } + + } + ) } + } + private fun handleProductDialog(products: List, dialogType: DialogType) { + requireContext().dialogBuilder( + "Select Product", + extras = dialogType, + items = products, + map = { it.product.label }, + onSelected = viewModel::handleProduct + ).setOnDismissListener { viewModel.hideDialog() }.create().show() + } + + private fun bindToTaxUpdatedEvents(){ + launch { + viewModel.stateFlow.bindTo( + TaxUpdateViewModel.State::updatedTaxId + ) { id -> + if (id.isNotNullOrBlank()) { + return@bindTo Toast.makeText( + requireContext(), + "Category with id [${id}] was updated", + Toast.LENGTH_SHORT + ).show() + } + } + } } -} \ No newline at end of file +} diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/TaxUpdateViewModel.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/TaxUpdateViewModel.kt index 0f1470e..059ee54 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/TaxUpdateViewModel.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/TaxUpdateViewModel.kt @@ -2,36 +2,39 @@ package com.godaddy.commerce.services.sample.catalog.tax.update -import android.os.Bundle -import androidx.core.os.bundleOf import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.godaddy.commerce.catalog.CatalogIntents -import com.godaddy.commerce.catalog.TaxConstants import com.godaddy.commerce.catalog.TaxParams import com.godaddy.commerce.catalog.model.CatalogProduct import com.godaddy.commerce.catalog.model.CatalogTax -import com.godaddy.commerce.catalog.models.* import com.godaddy.commerce.common.DataSource -import com.godaddy.commerce.services.sample.catalog.onSuccess -import com.godaddy.commerce.services.sample.common.extensions.onComplete -import com.godaddy.commerce.services.sample.common.extensions.onError +import com.godaddy.commerce.provider.catalog.CatalogContract +import com.godaddy.commerce.sdk.catalog.ProductParamsExt +import com.godaddy.commerce.sdk.catalog.TaxParamsExt +import com.godaddy.commerce.sdk.catalog.deleteCatalogTax +import com.godaddy.commerce.sdk.catalog.getCatalogProducts +import com.godaddy.commerce.sdk.catalog.getCatalogTax +import com.godaddy.commerce.sdk.catalog.patchCatalogTax +import com.godaddy.commerce.sdk.util.isNotNullOrBlank import com.godaddy.commerce.services.sample.common.extensions.subscribeOnUpdates -import com.godaddy.commerce.services.sample.common.extensions.toSimpleMoney +import com.godaddy.commerce.services.sample.common.util.DEFAULT_CURRENCY_CODE import com.godaddy.commerce.services.sample.common.viewmodel.CommonState import com.godaddy.commerce.services.sample.common.viewmodel.CommonViewModel import com.godaddy.commerce.services.sample.common.viewmodel.ToolbarState import com.godaddy.commerce.services.sample.di.CommerceDependencyProvider import com.godaddy.commerce.services.sample.di.CommerceDependencyProvider.getCatalogService -import com.godaddy.commerce.taxes.models.* +import com.godaddy.commercecore.models.Amount +import com.godaddy.commercecore.models.Classification +import com.godaddy.commercecore.models.Money +import com.godaddy.commercecore.models.Override +import com.godaddy.commercecore.models.OverrideRate +import com.godaddy.commercecore.models.Percentage +import com.godaddy.commercecore.models.Tax import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import kotlinx.coroutines.suspendCancellableCoroutine -import timber.log.Timber -import java.util.* class TaxUpdateViewModel( private val savedStateHandle: SavedStateHandle @@ -42,6 +45,7 @@ class TaxUpdateViewModel( init { fetchTax() + loadProducts() CommerceDependencyProvider.getContext() .subscribeOnUpdates(CatalogIntents.ACTION_TAXES_CHANGED) // refresh only when current tax is updated @@ -51,114 +55,259 @@ class TaxUpdateViewModel( } private fun fetchTax() { - viewModelScope.launch { + execute { val service = catalogServiceClient.getService().getOrThrow() - - val params = bundleOf(TaxParams.DATA_SOURCE to DataSource.REMOTE_IF_EMPTY) - val response = suspendCancellableCoroutine { - service.getCatalogTax(id, params, it.onSuccess(), it.onError()) - } ?: return@launch + val request = TaxParamsExt.toBundle( + taxId = id, + dataSource = DataSource.REMOTE_IF_EMPTY, + includeClassification = true, + includeOverrides = true, + ) + val response = service.getCatalogTax(id.orEmpty(), request) update { - copy( - updatedTaxLabel = response.tax.label, + val tax = requireNotNull(response?.tax) + + val classifications = tax.classifications.orEmpty() + val selectedClassification = classifications.firstOrNull() + val classificationLabel = selectedClassification?.label.orEmpty() + val classificationProductIds = selectedClassification?.productIds.orEmpty() - toolbarState = toolbarState.copy(title = "Update Tax: ${response.tax.label}") + val overrides = tax.overrides.orEmpty() + val selectedOverride = overrides.firstOrNull() + val overrideLabel = selectedOverride?.label.orEmpty() + val customRate = selectedOverride?.customRate + val overrideProductIds = selectedOverride?.productIds.orEmpty() + copy( + label = tax.label, + amount = tax.amount, + percentage = tax.percentage, + status = tax.status, + selectedClassification = selectedClassification, + availableClassifications = classifications, + classificationLabel = classificationLabel, + classificationProductIds = classificationProductIds, + selectedOverride = selectedOverride, + availableOverrides = overrides, + overrideLabel = overrideLabel, + overrideProductIds = overrideProductIds, + customRate = customRate, + taxType = if (tax.amount != null) "Amount" else "Percentage", + toolbarState = toolbarState.copy(title = "Update Tax: ${tax.label}") ) } } } - fun onNameChanged(value: String) { - update { copy(updatedTaxLabel = value) } + fun onLabelChanged(value: String) { + update { copy(label = value) } } - - private fun updateName(block: CatalogTax.() -> CatalogTax) { - state.tax?.tax ?: return - update { copy(tax = block(tax!!)) } + fun onRatePercentageChanged(value: String) { + update { copy(percentage = Percentage(value)) } + } + fun onAmountChanged(value: String) { + value.toLongOrNull()?.let { + update { + copy(amount = Amount(Money(currencyCode = DEFAULT_CURRENCY_CODE, it))) + } + } + } + fun onTaxOverrideLabelChanged(value: String) { + update { copy(overrideLabel = value) } + } + fun onTaxOverrideCustomRateChanged(value: String) { + update { copy(customRate = OverrideRate(ratePercentage = Percentage(value)))} + } + fun onTaxClassificationLabelChanged(value: String) { + update { copy(classificationLabel = value) } + } + fun onTaxTypeChange(position: Int) { + update { copy( + taxType = state.types[position] + ) } + } + private fun loadProducts(query: String? = null) { + execute { + val service = catalogServiceClient.getService().getOrThrow() + val bundle = ProductParamsExt.toBundle( + dataSource = DataSource.REMOTE_IF_EMPTY, + pageOffset = DEFAULT_TAX_PRODUCTS_PAGE_OFFSET, + pageSize = DEFAULT_TAX_PRODUCTS_PAGE_SIZE, + sortBy = CatalogContract.Product.Columns.UPDATED_AT, + searchTerm = query, + ) + val response = service.getCatalogProducts(bundle) + val products = response?.products.orEmpty().toSet() + update { copy (allProducts = products) } + } + } + fun switchTaxOverride(isShow: Boolean) { + update { copy(updateTaxOverride = isShow) } + } + fun switchTaxClassification(isShow: Boolean) { + update { copy(updateTaxClassification = isShow) } + } + fun hideDialog(){ + update{ copy(dialogType = DialogType.NO_SHOW) } + } + fun addClassificationProduct(){ + update { + val classificationProducts = allProducts.filter { + !classificationProductIds.contains(it.product.id) + } + copy( + dialogType = DialogType.ADD_CLASSIFICATION, + dialogList = classificationProducts + ) + } + } + fun removeClassificationProduct(){ + update { + val classificationProducts = allProducts.filter { + classificationProductIds.contains(it.product.id) + } + copy( + dialogType = DialogType.REMOVE_CLASSIFICATION, + dialogList = classificationProducts + ) + } + } + fun addOverrideProduct(){ + update { + val overrideProducts = allProducts.filter { + !overrideProductIds.contains(it.product.id) + } + copy( + dialogType = DialogType.ADD_OVERRIDE, + dialogList = overrideProducts + ) + } + } + fun removeOverrideProduct(){ + update { + val overrideProducts = allProducts.filter { + overrideProductIds.contains(it.product.id) + } + copy( + dialogType = DialogType.REMOVE_OVERRIDE, + dialogList = overrideProducts + ) + } + } + fun handleProduct(catalogProduct: CatalogProduct, dialogType: DialogType) { + val id = catalogProduct.product.id.toString() + if (dialogType == DialogType.ADD_CLASSIFICATION){ + update { copy (classificationProductIds = state.classificationProductIds.plus(id)) } + } + else if (state.dialogType == DialogType.ADD_OVERRIDE){ + update { copy (overrideProductIds = state.overrideProductIds.plus(id)) } + } + else if (dialogType == DialogType.REMOVE_CLASSIFICATION){ + update { copy (classificationProductIds = state.classificationProductIds.minus(id)) } + } + else if (dialogType == DialogType.REMOVE_OVERRIDE){ + update { copy (overrideProductIds = state.overrideProductIds.minus(id)) } + } } - -// fun onRateNameChanged(value: String) { -// updateTaxRate { copy(name = value) } -// } -// -// fun onAmountTypeChanged(position: Int) { -// updateTaxRate { copy(amountType = state.amountTypes.getOrNull(position)) } -// } -// -// fun onRatePercentageChanged(value: String) { -// updateTaxRate { copy(ratePercentage = value) } -// } -// -// fun onAmountChanged(value: String) { -// updateTaxRate { copy(amount = value.toLongOrNull().toSimpleMoney()) } -// } fun update() { execute { - val tax = requireNotNull(state.tax) { "Tax must be loaded" } + val classifications = if (state.availableClassifications.isNotEmpty()) listOf( + Classification( + id = state.selectedClassification?.id, + label = state.classificationLabel, + productIds = state.classificationProductIds + ) + ) else emptyList() - val catalogService = catalogServiceClient.getService().getOrThrow() + val overrides = if (state.availableOverrides.isNotEmpty()) listOf( + Override( + id = state.selectedOverride?.id, + label = state.overrideLabel, + customRate = state.customRate, + productIds = state.overrideProductIds + ) + ) else emptyList() - val response = suspendCancellableCoroutine { - catalogService.patchCatalogTax(id, tax, Bundle.EMPTY, it.onSuccess(), it.onError()) - } + val percentage = if (state.taxType == "Percentage") state.percentage else null + val amount = if (state.taxType == "Amount") state.amount else null + val tax = Tax( + id = id, + status = requireNotNull(state.status), + label = requireNotNull(state.label), + percentage = percentage, + amount = amount, + classifications = classifications, + overrides = overrides, + ) + + val request = CatalogTax(tax) + val catalogService = catalogServiceClient.getService().getOrThrow() + val response = catalogService.patchCatalogTax(requireNotNull(id), request) + update { copy(updatedTaxId = response?.tax?.id) } sendEffect(Effect.ShowToast("Tax was updated: ${response?.tax?.id}")) } } - fun delete() { execute { val catalogService = catalogServiceClient.getService().getOrThrow() - - // to remove tax need to remove tax association only - - // get tax association -// val items = suspendCancellableCoroutine { -// val associationParams = bundleOf( -// TaxParams.TAX_ID to id, -// TaxParams.DATA_SOURCE to DataSource.REMOTE_IF_EMPTY -// ) -// catalogService.getTaxAssociations(associationParams, it.onSuccess(), it.onError()) -// } -// -// Timber.d("Association items: ${items?.associations}") -// val association = -// requireNotNull(items?.associations?.firstOrNull()) { "There are no associations for current tax" } -// -// // remove tax association by id -// suspendCancellableCoroutine { -// catalogService.deleteTaxAssociation( -// association.id?.toString(), -// Bundle.EMPTY, -// it.onComplete(), -// it.onError() -// ) -// } - + val request = TaxParamsExt.toBundle( + taxId = TaxParams.TAX_ID, + dataSource = DataSource.REMOTE_IF_EMPTY, + includeOverrides = true, + includeClassification = true, + ) + val response = catalogService.deleteCatalogTax(id.orEmpty(), request) + } sendEffect(Effect.ShowToast("Tax $id was removed")) sendEffect(Effect.PopScreen) - } } - private fun updateTax(block: CatalogTax.() -> CatalogTax) { - state.tax ?: return - update { copy(tax = block(tax!!)) } - } - -// private fun updateTaxRate(block: TaxRate.() -> TaxRate) { -// updateTax { copy(taxRates = isltOf(block(taxRates!!.first()))) } -// } - data class State( override val commonState: CommonState = CommonState(), override val toolbarState: ToolbarState = ToolbarState(title = "Update Tax"), - val tax: CatalogTax? = null, - val updatedTaxLabel: String? = null, -// val overrideAssociation: TaxOverrideAssociation? = null, - val amountTypes: List = TaxConstants.AmountType.values.toList(), - val products: List = emptyList(), -// val showTaxAssociationProductDialog: Boolean = false, -// val showTaxOverrideAssociationProductDialog: Boolean = false, + // Tax Fields + val updatedTaxId: String? = null, + val status: String? = null, + val label: String? = null, + val amount: Amount? = null, + val percentage: Percentage? = null, + val createdId: String? = null, + val types: List = listOf("Amount", "Percentage"), + val taxType: String = "Amount", + + val availableOverrides: List = emptyList(), + val availableClassifications: List = emptyList(), + val selectedOverride: Override? = null, + val selectedClassification: Classification? = null, + + // Override Fields + val overrideLabel: String? = null, + val customRate: OverrideRate? = null, + val updateTaxOverride: Boolean = false, + + // Classification Fields + val classificationLabel: String? = null, + val updateTaxClassification: Boolean = false, + + // Fields for handling product mapping + val classificationProductIds: Set = emptySet(), + val overrideProductIds: Set = emptySet(), + + val allProducts: Set = emptySet(), + val dialogList: List = emptyList(), + val dialogType: DialogType? = null, ) : ViewModelState + + companion object{ + private const val DEFAULT_TAX_PRODUCTS_PAGE_SIZE = 100 + private const val DEFAULT_TAX_PRODUCTS_PAGE_OFFSET = 0 + } + enum class DialogType{ + ADD_CLASSIFICATION, + ADD_OVERRIDE, + REMOVE_CLASSIFICATION, + REMOVE_OVERRIDE, + NO_SHOW, + } } diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/association/TaxAssociationFragment.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/association/TaxAssociationFragment.kt deleted file mode 100644 index 5f8ede3..0000000 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/association/TaxAssociationFragment.kt +++ /dev/null @@ -1,51 +0,0 @@ -//package com.godaddy.commerce.services.sample.catalog.tax.update.association -// -//import android.os.Bundle -//import android.view.View -//import androidx.fragment.app.viewModels -//import com.godaddy.commerce.catalog.models.Product -//import com.godaddy.commerce.services.sample.R -//import com.godaddy.commerce.services.sample.catalog.tax.mapToUiItems -//import com.godaddy.commerce.services.sample.common.extensions.bindTo -//import com.godaddy.commerce.services.sample.common.extensions.dialogBuilder -//import com.godaddy.commerce.services.sample.common.extensions.launch -//import com.godaddy.commerce.services.sample.common.extensions.observableField -//import com.godaddy.commerce.services.sample.common.view.CommonFragment -//import com.godaddy.commerce.services.sample.common.view.bindOnCommonViewModelUpdates -//import com.godaddy.commerce.services.sample.databinding.TaxAssociationFragmentBinding -// -//class TaxAssociationFragment : -// CommonFragment(R.layout.tax_association_fragment) { -// -// -// private val viewModel: TaxAssociationViewModel by viewModels() -// -// val associationItems by observableField( -// stateFlow = { viewModel.stateFlow }, -// map = { -// association?.items.orEmpty() -// .map { it.mapToUiItems { viewModel.removeProductFromTax(it) } } -// }, -// keySelector = { it.association?.items }) -// -// override fun onViewCreated(view: View, savedInstanceState: Bundle?) { -// super.onViewCreated(view, savedInstanceState) -// bindOnCommonViewModelUpdates(viewModel) -// launch { -// viewModel.stateFlow.bindTo( -// map = { products to showProductDialog }, -// update = ::showTaxAssociationProductDialog -// ) -// } -// } -// -// private fun showTaxAssociationProductDialog(pair: Pair, Boolean>) { -// if (pair.second.not()) return -// requireContext().dialogBuilder( -// "Add Product to Tax", -// items = pair.first, -// map = { it.name }, -// onSelected = viewModel::addProductToTax -// ).setOnDismissListener { viewModel.hideProductsDialog() }.create().show() -// } -//} \ No newline at end of file diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/association/TaxAssociationViewModel.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/association/TaxAssociationViewModel.kt deleted file mode 100644 index f83c0a9..0000000 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/association/TaxAssociationViewModel.kt +++ /dev/null @@ -1,164 +0,0 @@ -//package com.godaddy.commerce.services.sample.catalog.tax.update.association -// -//import android.os.Bundle -//import androidx.core.os.bundleOf -//import androidx.lifecycle.SavedStateHandle -//import androidx.lifecycle.viewModelScope -//import co.poynt.api.model.Business -//import co.poynt.api.model.Product -import com.godaddy.commerce.catalog.ICatalogService -//import com.godaddy.commerce.catalog.ProductParams -//import com.godaddy.commerce.catalog.TaxConstants -//import com.godaddy.commerce.catalog.TaxParams -//import com.godaddy.commerce.catalog.model.CatalogProduct -//import com.godaddy.commerce.catalog.model.CatalogProducts -//import com.godaddy.commerce.common.DataSource -//import com.godaddy.commerce.sdk.business.BusinessParams -//import com.godaddy.commerce.services.sample.catalog.onSuccess -//import com.godaddy.commerce.services.sample.common.extensions.onError -//import com.godaddy.commerce.services.sample.common.extensions.onSuccess -//import com.godaddy.commerce.services.sample.common.viewmodel.CommonState -//import com.godaddy.commerce.services.sample.common.viewmodel.CommonViewModel -//import com.godaddy.commerce.services.sample.common.viewmodel.ToolbarState -//import com.godaddy.commerce.services.sample.di.CommerceDependencyProvider -//import com.godaddy.commerce.services.sample.di.CommerceDependencyProvider.getCatalogService -//import com.godaddy.commerce.taxes.models.AssociationItems -//import com.godaddy.commerce.taxes.models.TaxAssociation -//import kotlinx.coroutines.launch -//import kotlinx.coroutines.suspendCancellableCoroutine -//import java.util.* -// -//class TaxAssociationViewModel( -// private val savedStateHandle: SavedStateHandle -//) : CommonViewModel(State()) { -// -// private val catalogServiceClient = getCatalogService(viewModelScope) -// private val id get() = savedStateHandle.get("id") -// -// init { -// viewModelScope.launch { -// val service = catalogServiceClient.getService().getOrThrow() -// fetchTaxAssociations(service) -// } -// } -// -// private fun fetchTaxAssociations(service: ICatalogService) { -// viewModelScope.launch { -// val items = suspendCancellableCoroutine?> { -// val associationParams = bundleOf( -// TaxParams.TAX_ID to id, -// TaxParams.DATA_SOURCE to DataSource.REMOTE_IF_EMPTY -// ) -//// service.getTaxAssociations(associationParams, it.onSuccess(), it.onError()) -// } -//// update { -//// val association = items?.associations?.firstOrNull() ?: throw IllegalStateException( -//// "There are no associations for current tax" -//// ) -//// copy( -//// associationId = requireNotNull(association.id?.toString()), -//// association = association.let { -//// TaxAssociation(name = it.name, type = it.type, items = it.items) -//// }) -//// } -// } -// } -// -// fun showProductDialog() { -// execute { -// if (state.products.isEmpty()) { -// val params = bundleOf( -// // recommended data source is REMOTE_IF_EMPTY. -// ProductParams.DATA_SOURCE to DataSource.REMOTE_IF_EMPTY, -// // pagination is required otherwise exception can be thrown. -// ProductParams.PAGE_SIZE to 100, -// ProductParams.PAGE_OFFSET to 0, -// ) -// val service = catalogServiceClient.getService().getOrThrow() -// val response = suspendCancellableCoroutine { -// service.getCatalogProducts(params, it.onSuccess(), it.onError()) -// } -// update { copy(products = response?.products.orEmpty(), showProductDialog = true) } -// } else { -// update { copy(showProductDialog = true) } -// } -// } -// } -// -// fun hideProductsDialog() { -// update { copy(showProductDialog = false) } -// } -// -// fun addProductToTax(product: CatalogProduct) { -// hideProductsDialog() -// execute { -// val association = state.association -// requireNotNull(association) { "Association is required to add product." } -// -// val items = association.items.orEmpty().toMutableList() -// // make sure that items list has only store item or (product and categories) items -// items.removeAll { it.type == TaxConstants.Association.TYPE_STORE } -// // add new product item to association -// items.add( -// AssociationItems(TaxConstants.Association.TYPE_PRODUCT, id = UUID.fromString(product.product.id)) -// ) -// update { copy(association = association.copy(items = items)) } -// } -// } -// -// fun removeProductFromTax(itemId: UUID) { -// execute { -// val association = state.association -// requireNotNull(association) { "Association is required." } -// -// val items = association.items.orEmpty().toMutableList() -// items.removeAll { it.id == itemId } -// // if there are no items in association then create store item association. -// if (items.isEmpty()) { -// items.add( -// AssociationItems(TaxConstants.Association.TYPE_STORE, id = getStoreId()) -// ) -// } -// update { copy(association = association.copy(items = items)) } -// } -// } -// -// fun update() { -// execute { -// val catalogService = catalogServiceClient.getService().getOrThrow() -// val association = requireNotNull(state.association) { "Association is not loaded" } -// val response = suspendCancellableCoroutine { -// catalogService.patchTaxAssociation( -// state.associationId, -// association, -// Bundle.EMPTY, -// it.onSuccess(), -// it.onError() -// ) -// } -// sendEffect(Effect.ShowToast("Item was updated")) -// } -// } -// -// private suspend fun getStoreId(): UUID? { -// val service = -// CommerceDependencyProvider.getBusinessService(viewModelScope).getService().getOrThrow() -// val result = suspendCancellableCoroutine { -// service.getBusiness( -// bundleOf(BusinessParams.FROM_CLOUD to false), -// it.onSuccess(), -// it.onError() -// ) -// } -// return result.stores.firstOrNull()?.id -// } -// -// data class State( -// override val commonState: CommonState = CommonState(), -// override val toolbarState: ToolbarState = ToolbarState(title = "Update Tax Association"), -// val products: List = emptyList(), -// val showProductDialog: Boolean = false, -// val associationId: String? = null, -// val association: TaxAssociation? = null, -// ) : ViewModelState -//} diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/override/TaxOverrideAssociationFragment.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/override/TaxOverrideAssociationFragment.kt deleted file mode 100644 index 3c5b26b..0000000 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/override/TaxOverrideAssociationFragment.kt +++ /dev/null @@ -1,70 +0,0 @@ -//package com.godaddy.commerce.services.sample.catalog.tax.update.override -// -//import android.os.Bundle -//import android.view.View -//import androidx.fragment.app.viewModels -//import com.godaddy.commerce.catalog.models.Product -//import com.godaddy.commerce.services.sample.R -//import com.godaddy.commerce.services.sample.catalog.tax.mapToUiItems -//import com.godaddy.commerce.services.sample.catalog.tax.update.override.TaxOverrideAssociationViewModel.State -//import com.godaddy.commerce.services.sample.common.extensions.bindTo -//import com.godaddy.commerce.services.sample.common.extensions.dialogBuilder -//import com.godaddy.commerce.services.sample.common.extensions.launch -//import com.godaddy.commerce.services.sample.common.extensions.observableField -//import com.godaddy.commerce.services.sample.common.view.CommonFragment -//import com.godaddy.commerce.services.sample.common.view.bindOnCommonViewModelUpdates -//import com.godaddy.commerce.services.sample.databinding.TaxOverrideAssociationFragmentBinding -// -//class TaxOverrideAssociationFragment : -// CommonFragment(R.layout.tax_override_association_fragment) { -// -// -// private val viewModel: TaxOverrideAssociationViewModel by viewModels() -// -// val overrideRateName by observableField({ viewModel.stateFlow }, { overrideAssociation?.name }) -// val overrideAmount by observableField( -// { viewModel.stateFlow }, -// { overrideAssociation?.overrideValue?.amount?.value?.toString() }) -// val overrideRate by observableField( -// { viewModel.stateFlow }, -// { overrideAssociation?.overrideValue?.ratePercentage }) -// val overrideType by observableField( -// { viewModel.stateFlow }, -// { overrideAssociation?.overrideValue?.amountType?.let { amountTypes.indexOf(it) } }) -// -// val overrideItems by observableField( -// stateFlow = { viewModel.stateFlow }, -// map = { -// overrideAssociation?.items.orEmpty().map { -// it.mapToUiItems { viewModel.removeOverrideForProduct(it) } -// } -// }, -// keySelector = { it.overrideAssociation?.items }) -// -// val types by observableField( -// stateFlow = { viewModel.stateFlow }, -// map = State::amountTypes -// ) -// -// -// override fun onViewCreated(view: View, savedInstanceState: Bundle?) { -// super.onViewCreated(view, savedInstanceState) -// bindOnCommonViewModelUpdates(viewModel) -// launch { -// viewModel.stateFlow.bindTo( -// map = { products to showProductDialog }, -// update = ::showTaxAssociationProductDialog -// ) -// } -// } -// -// private fun showTaxAssociationProductDialog(pair: Pair, Boolean>) { -// if (pair.second.not()) return -// requireContext().dialogBuilder( -// "Add override tax for product", -// items = pair.first, -// map = { it.name }, -// onSelected = viewModel::addOverrideForProduct -// ).setOnDismissListener { viewModel.hideProductsDialog() }.create().show() -// } -//} \ No newline at end of file diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/override/TaxOverrideAssociationViewModel.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/override/TaxOverrideAssociationViewModel.kt deleted file mode 100644 index 2747407..0000000 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/catalog/tax/update/override/TaxOverrideAssociationViewModel.kt +++ /dev/null @@ -1,212 +0,0 @@ -//package com.godaddy.commerce.services.sample.catalog.tax.update.override -// -//import android.os.Bundle -//import androidx.core.os.bundleOf -//import androidx.lifecycle.SavedStateHandle -//import androidx.lifecycle.viewModelScope -//import co.poynt.api.model.Business -//import com.godaddy.commerce.catalog.ICatalogService -//import com.godaddy.commerce.catalog.ProductParams -//import com.godaddy.commerce.catalog.TaxConstants -//import com.godaddy.commerce.catalog.TaxParams -//import com.godaddy.commerce.catalog.models.Product -//import com.godaddy.commerce.catalog.models.Products -//import com.godaddy.commerce.common.DataSource -//import com.godaddy.commerce.sdk.business.BusinessParams -//import com.godaddy.commerce.services.sample.catalog.onSuccess -//import com.godaddy.commerce.services.sample.common.extensions.onComplete -//import com.godaddy.commerce.services.sample.common.extensions.onError -//import com.godaddy.commerce.services.sample.common.extensions.onSuccess -//import com.godaddy.commerce.services.sample.common.extensions.toSimpleMoney -//import com.godaddy.commerce.services.sample.common.viewmodel.CommonState -//import com.godaddy.commerce.services.sample.common.viewmodel.CommonViewModel -//import com.godaddy.commerce.services.sample.common.viewmodel.ToolbarState -//import com.godaddy.commerce.services.sample.di.CommerceDependencyProvider -//import com.godaddy.commerce.services.sample.di.CommerceDependencyProvider.getCatalogService -//import com.godaddy.commerce.taxes.models.AssociationItems -//import com.godaddy.commerce.taxes.models.TaxOverrideAssociation -//import com.godaddy.commerce.taxes.models.TaxOverrideAssociationOverrideValue -//import com.godaddy.commerce.taxes.models.TaxOverrideAssociations -//import kotlinx.coroutines.launch -//import kotlinx.coroutines.suspendCancellableCoroutine -//import java.util.* -// -//class TaxOverrideAssociationViewModel( -// private val savedStateHandle: SavedStateHandle -//) : CommonViewModel(State()) { -// -// private val catalogServiceClient = getCatalogService(viewModelScope) -// private val id get() = savedStateHandle.get("id") -// -// init { -// viewModelScope.launch { -// val service = catalogServiceClient.getService().getOrThrow() -// fetchTaxAssociations(service) -// } -// } -// -// private fun fetchTaxAssociations(service: ICatalogService) { -// viewModelScope.launch { -// val items = suspendCancellableCoroutine { -// val associationParams = bundleOf( -// TaxParams.TAX_ID to id, -// TaxParams.DATA_SOURCE to DataSource.REMOTE_IF_EMPTY -// ) -// service.getTaxOverrideAssociations(associationParams, it.onSuccess(), it.onError()) -// } -// update { -// val association = items?.associations?.firstOrNull() ?: throw IllegalStateException( -// "There are no override associations for current tax" -// ) -// copy( -// associationId = requireNotNull(association.id?.toString()), -// overrideAssociation = association.let { -// TaxOverrideAssociation( -// name = it.name, -// type = it.type, -// items = it.items, -// overrideValue = it.overrideValue -// ) -// } -// ) -// } -// } -// } -// -// fun showProductDialog() { -// execute { -// if (state.products.isEmpty()) { -// val params = bundleOf( -// // recommended data source is REMOTE_IF_EMPTY. -// ProductParams.DATA_SOURCE to DataSource.REMOTE_IF_EMPTY, -// // pagination is required otherwise exception can be thrown. -// ProductParams.PAGE_SIZE to 100, -// ProductParams.PAGE_OFFSET to 0, -// ) -// val service = catalogServiceClient.getService().getOrThrow() -// val response = suspendCancellableCoroutine { -// service.getProducts(params, it.onSuccess(), it.onError()) -// } -// update { copy(products = response?.products.orEmpty(), showProductDialog = true) } -// } else { -// update { copy(showProductDialog = true) } -// } -// } -// } -// -// fun hideProductsDialog() { -// update { copy(showProductDialog = false) } -// } -// -// -// fun onTaxOverrideRateNameChanged(value: String) { -// updateTaxOverrideRate { copy(name = value) } -// } -// -// fun onTaxOverrideRateAmountChanged(value: String) { -// updateTaxOverrideValue { copy(amount = value.toLongOrNull().toSimpleMoney()) } -// } -// -// fun onTaxOverrideRatePercentageChanged(value: String) { -// updateTaxOverrideValue { copy(ratePercentage = value) } -// } -// -// fun onTaxOverrideRateAmountTypeChanged(value: Int) { -// updateTaxOverrideValue { copy(amountType = state.amountTypes.getOrNull(value)) } -// } -// -// fun addOverrideForProduct(product: Product) { -// hideProductsDialog() -// execute { -// val association = state.overrideAssociation -// requireNotNull(association) { "Override Association is required to add product." } -// -// val items = association.items.orEmpty().toMutableList() -// -// // add new product item to association -// items.add( -// AssociationItems(TaxConstants.Association.TYPE_PRODUCT, id = product.productId) -// ) -// update { copy(overrideAssociation = association.copy(items = items)) } -// } -// } -// -// fun removeOverrideForProduct(itemId: UUID) { -// execute { -// val association = state.overrideAssociation -// requireNotNull(association) { "Override Association is required." } -// -// val items = association.items.orEmpty().toMutableList() -// items.removeAll { it.id == itemId } -// // if there are no items in association then keep it empty and then delete on update request. -// update { copy(overrideAssociation = association.copy(items = items)) } -// } -// } -// -// fun update() { -// execute { -// val catalogService = catalogServiceClient.getService().getOrThrow() -// val association = -// requireNotNull(state.overrideAssociation) { "Association is not loaded" } -// -// // Delete association if there are no items -// if (association.items.isNullOrEmpty()) { -// suspendCancellableCoroutine { -// catalogService.deleteTaxOverrideAssociation( -// state.associationId, -// Bundle.EMPTY, -// it.onComplete(), -// it.onError() -// ) -// } -// sendEffect(Effect.ShowToast("Item was deleted")) -// sendEffect(Effect.PopScreen) -// } else { -// suspendCancellableCoroutine { -// catalogService.patchTaxOverrideAssociation( -// state.associationId, -// association, -// Bundle.EMPTY, -// it.onSuccess(), -// it.onError() -// ) -// }?.id -// sendEffect(Effect.ShowToast("Item was updated")) -// } -// -// } -// } -// -// -// private suspend fun getStoreId(): UUID? { -// val service = -// CommerceDependencyProvider.getBusinessService(viewModelScope).getService().getOrThrow() -// val result = suspendCancellableCoroutine { -// service.getBusiness( -// bundleOf(BusinessParams.FROM_CLOUD to false), -// it.onSuccess(), -// it.onError() -// ) -// } -// return result.stores.firstOrNull()?.id -// } -// -// private fun updateTaxOverrideRate(block: TaxOverrideAssociation.() -> TaxOverrideAssociation) { -// state.overrideAssociation ?: return -// update { copy(overrideAssociation = block(overrideAssociation!!)) } -// } -// -// private fun updateTaxOverrideValue(block: TaxOverrideAssociationOverrideValue.() -> TaxOverrideAssociationOverrideValue) { -// updateTaxOverrideRate { copy(overrideValue = block(overrideValue!!)) } -// } -// -// data class State( -// override val commonState: CommonState = CommonState(), -// override val toolbarState: ToolbarState = ToolbarState(title = "Update Tax Override Association"), -// val products: List = emptyList(), -// val overrideAssociation: TaxOverrideAssociation? = null, -// val associationId: String? = null, -// val amountTypes: List = TaxConstants.AmountType.values.toList(), -// val showProductDialog: Boolean = false, -// ) : ViewModelState -//} diff --git a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/common/extensions/Extensions.kt b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/common/extensions/Extensions.kt index 335e244..80361c1 100644 --- a/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/common/extensions/Extensions.kt +++ b/commerce-services-sample/src/main/java/com/godaddy/commerce/services/sample/common/extensions/Extensions.kt @@ -61,6 +61,23 @@ fun Context.dialogBuilder( return builder } +fun Context.dialogBuilder( + title: String, + extras: R, + items: List, + map: (T) -> String?, + onSelected: (T, R) -> Unit +): AlertDialog.Builder { + val builder = AlertDialog.Builder(this) + builder.setTitle(title) + builder.setItems(items.map(map).toTypedArray()) { dialog, which -> + onSelected(items[which], extras) + dialog.dismiss() + } + return builder +} + + suspend fun StateFlow.bindTo( map: T.() -> K, keySelector: ((T) -> Any?)? = null, diff --git a/commerce-services-sample/src/main/res/layout/tax_association_fragment.xml b/commerce-services-sample/src/main/res/layout/tax_association_fragment.xml deleted file mode 100644 index 2142d64..0000000 --- a/commerce-services-sample/src/main/res/layout/tax_association_fragment.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -