package com.app.features.checkout
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.app.base.viewmodel.AppBaseViewModel
import com.app.core.enums.DeliveryTime
import com.app.core.enums.ShippingType
import com.app.core.errors.AppError
import com.app.core.graphql.errorhandling.NetworkErrorHandlingImpl
import com.app.core.models.*
import com.app.core.repositories.AuthenticationRepo
import com.app.core.repositories.CheckoutRepository
import com.app.core.repositories.StoreConfigRepo
import com.app.core.repositories.UserRepository
import com.app.core.usecases.*
import com.app.core.utils.LocaleManager
import com.app.features.checkout.models.AppTempAddress
import com.app.features.checkout.models.AppTempContactInfo
import com.app.features.checkout.models.UIDeliveryAddress
import com.app.features.checkout.usecases.*
import com.payfort.sdk.android.dependancies.base.FortInterfaces
import com.payfort.sdk.android.dependancies.models.FortRequest
import com.paymob.acceptsdk.IntentConstants.*
import com.robusta.bootstrap.data.api.observable.addTo
import com.robusta.bootstrap.service.schedulers.SchedulersService
import com.robusta.bootstrap.service.session.SessionService
import com.robustastudio.checkout_feat.CartPriceDetails
import com.robustastudio.ecommerce.analytics_feat.AnalyticsConstants
import com.robustastudio.ecommerce.analytics_feat.AnalyticsService
import com.robustastudio.magentocore.model.IAvailablePaymentMethod
import com.robustastudio.magentocore.model.product.ProductSectionType
import com.robustastudio.magentocore.utils.ProductInfoCache
import com.robustastudio.networkclient.utils.Errors
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Single
import org.koin.core.component.inject
import java.util.HashMap
const val FREE_PAYMENT_KEY = "free"
class CheckoutViewModel(
private val checkoutRepository: CheckoutRepository,
private val sessionService: SessionService,
schedulersService: SchedulersService,
private val getUserAddressesUseCase: GetUserAddressesUseCase,
private val getDefaultUserLocationUseCase: GetDefaultUserLocationUseCase,
private val getAvailableDeliveryMethodsUseCase: GetAvailableDeliveryMethodsUseCase,
private val setShippingMethodUseCase: SetShippingMethodUseCase,
private val setDeliveryTimeUseCase: SetDeliveryTimeUseCase,
private val getAvailablePaymentMethodsUseCase: GetAvailablePaymentMethodsUseCase,
private val authenticationRepo: AuthenticationRepo,
private val setPaymentMethodOnCartUseCase: SetPaymentMethodOnCartUseCase,
private val getAvailableRegionsUseCase: GetAvailableRegionsUseCase,
private val fetchGovernoratesUseCase: FetchGovernoratesUseCase,
private val proceedWithCreditCardUseCase: ProceedWithCreditCardUseCase,
private val proceedWithCashUseCase: ProceedWithCashUseCase,
private val fetchCartPriceDetailsUseCase: FetchCartPriceDetailsUseCase,
private val fetchDeliveryDatesUseCase: FetchDeliveryDatesUseCase,
private val fetchCartItemTypesUseCase: FetchCartItemTypesUseCase,
private val setOrderSourceOnCartUseCase: SetOrderSourceOnCartUseCase,
private val getPayFortSdkTokenUseCase: GetPayFortSdkTokenUseCase,
private val applyLoyaltyPointsUseCase: ApplyLoyaltyPointsUseCase,
private val getPaymobIframeUrlOrReferenceNumberUseCase: GetPaymobIframeUrlOrReferenceNumberUseCase,
private val userRepository: UserRepository,
private val getLoyaltyPointsExchangeRatesUseCase: GetLoyaltyPointsExchangeRatesUseCase,
private val storeConfigRepo: StoreConfigRepo,
) : AppBaseViewModel<CheckoutState, CheckoutEvent, CheckoutSideEffect>(schedulersService),
FortInterfaces.OnTnxProcessed {
override val stateMachine = getCheckoutStateMachine()
private val analyticsService: AnalyticsService by inject()
private var selectedDeliveryAddress: UIDeliveryAddress? = null
private var cachedUserAddresses:
List<UIDeliveryAddress
> = mutableListOf
()
private var selectedPaymentMethod: AppAvailablePaymentMethod? = null
private var availablePaymentMethods:
List<AppAvailablePaymentMethod
>? =
null
private var selectedShippingMethod: AppAvailableDeliveryMethod? = null
private var availableShippingMethods:
List<AppAvailableDeliveryMethod
>? =
null
private var selectedDeliveryDay: AppDeliveryDay? = null
var availableDeliveryDays:
List<AppDeliveryDay
>? =
null
private var selectedDeliveryTime: DeliveryTime = DeliveryTime.ASAP
private var selectedDeliveryInterval: AppDeliveryInterval? = null
private var availableDeliveryIntervals:
List<AppDeliveryInterval
>? =
null
private var selectedStore: AppShippingLocation? = null
private var availableCities:
List<AppRegion
>? =
null
private var guestContactInfo: AppTempContactInfo? = null
private var guestTempAddress: AppTempAddress? = null
private var cartPriceDetails: CartPriceDetails? = null
private var selectedShippingType: ShippingType = ShippingType.HOME
private var hasNonGroceryItems:
Boolean =
false
private var hasGroceryItems:
Boolean =
false
val defaultLocation = getDefaultUserLocationUseCase.execute()
private val _priceDetails = MutableLiveData<CartPriceDetails>()
val priceDetails: LiveData<CartPriceDetails>
get() = _priceDetails
private val _promoCodeState = MutableLiveData<PromoCodeState>(PromoCodeState.Input)
val promoCodeStateLiveData: LiveData<PromoCodeState>
get() = _promoCodeState
init {
analyticsService.logBeginCheckout()
analyticsService.logEventToFB(AnalyticsConstants.FACEBOOK_ANALYTICS_EVENTS.EVENT_INITIATE_CHECKOUT)
loadShippingDataForDelivery()
}
fun loadShippingDataForDelivery(shippingAddress: UIDeliveryAddress? = null) {
selectedShippingType = ShippingType.HOME
if (isGuest()) {
fetchGovernoratesUseCase.execute()
.zipWith(fetchCartPriceDetailsUseCase.execute()) { regions, prices ->
Pair(regions, prices)
}.compose(getIOTransformer()).doOnSubscribe {
//SHOW LOADING
handleEvent(CheckoutEvent.StartLoadingCheckoutData)
}.subscribe({
cartPriceDetails = it.second
availableCities = it.first
// handleEvent(
// CheckoutEvent.OnShippingDataLoaded(
// availableDeliveryDays = emptyList(),
// availableAddresses = emptyList(),
// isGuest = true,
// selectedShippingType = ShippingType.HOME,
// cities = it.first.sortedBy { it.cities.isEmpty() },
// cartPriceDetails = it.second,
// electronicsCount = electronicsCount,
// groceriesCount = groceriesCount
// )
// )
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
}).addTo(compositeDisposable)
} else {
val location = getDefaultUserLocationUseCase.execute()
getUserAddressesUseCase.execute().zipWith(getAvailableRegionsUseCase.execute(), ::Pair)
.flatMap {
cachedUserAddresses = it.first.mapIndexed { index, address ->
val isInDeliveryZone = isAddressSupportedByStoreCode(
address = address,
storeCode = location?.availableStore?.storeCode!!,
it.second
)
UIDeliveryAddress(
isInDeliveryZone = isInDeliveryZone,
id = address.id,
addressName = if (address.addressName != null && address.addressName!!.isNotEmpty()) address.addressName else "Address #${index + 1}",
district = address.district,
city = address.city,
region = address.region,
apartment = address.apartment,
lastName = address.lastName,
firstName = address.firstName,
defaultShipping = address.defaultShipping,
floor = address.floor,
defaultBilling = address.defaultBilling,
building = address.building,
countryCode = address.countryCode,
customerNotes = address.customerNotes,
street = address.street,
phone = address.phone,
lat = address.lat,
lng = address.lng,
compound = address.compound,
)
}
if (cachedUserAddresses.isNotEmpty()) {
//WHAT IF THE SHIPPING ADDRESS IS NOT IN THE LOADED LIST?
selectedDeliveryAddress =
shippingAddress ?: cachedUserAddresses.first { it.isInDeliveryZone }
getAvailableDeliveryMethodsUseCase.execute(
selectedDeliveryAddress!!.id!!
).zipWith(
fetchDeliveryDatesUseCase.execute(
selectedDeliveryAddress!!.city!!.code,
selectedDeliveryAddress!!.district!!.id.toString()
)
) { methods:
List<AppAvailableDeliveryMethod
>, days:
List<AppDeliveryDay
> -
>
Pair(methods, days)
}
} else Single.just(
Pair(
emptyList(), emptyList()
)
)
}.flatMapCompletable {
availableShippingMethods = it.first
availableDeliveryDays = it.second
if (it.first.isNotEmpty() && it.second.isNotEmpty()) {
selectedShippingMethod =
it.first.filterNot { it.methodCode.contains("store") }.first()
selectedDeliveryDay = it.second.first()
availableDeliveryIntervals = selectedDeliveryDay!!.intervals
selectedDeliveryInterval = availableDeliveryIntervals!!.first()
setShippingMethodUseCase.execute(
selectedShippingMethod!!
).andThen(
setDeliveryTimeUseCase.execute(
selectedDeliveryDay!!.date, selectedDeliveryInterval!!.id
)
)
} else Completable.complete()
}.andThen(Single.zip(
fetchCartPriceDetailsUseCase.execute(),
fetchCartItemTypesUseCase.execute(),
getLoyaltyPointsExchangeRatesUseCase.execute()
) { price, items, pointsRate ->
Triple(price, items, pointsRate)
}).flatMap {
if (it.first.grandTotal == 0.0) {
selectedPaymentMethod = AppAvailablePaymentMethod(
FREE_PAYMENT_KEY, "", 0f
)
setPaymentMethodOnCartUseCase.execute(
AppAvailablePaymentMethod(
FREE_PAYMENT_KEY, "", 0f
)
).andThen(Single.just(it))
} else {
getAvailablePaymentMethodsUseCase.execute().flatMapCompletable { list ->
availablePaymentMethods = list
selectedPaymentMethod = list.first()
setPaymentMethodOnCartUseCase.execute(list.first())
}.andThen(Single.just(it))
}
}.compose(getIOTransformer()).doOnSubscribe {
//SHOW LOADING
handleEvent(CheckoutEvent.StartLoadingCheckoutData)
}.subscribe({
cartPriceDetails = it.first
hasNonGroceryItems =
it.second.contains(ProductSectionType.ELECTRONICS) || it.second.contains(
ProductSectionType.CLOTHING
)
hasNonGroceryItems =
it.second.contains(ProductSectionType.NON_ELECTRONICS)
handleEvent(
CheckoutEvent.OnShippingDataLoaded(
availableDeliveryDays = availableDeliveryDays,
availableAddresses = cachedUserAddresses,
isGuest = isGuest(),
hasNonGroceryItems = hasNonGroceryItems,
hasGroceryItems = hasGroceryItems,
selectedShippingType = ShippingType.HOME,
selectedDeliveryAddress = selectedDeliveryAddress!!,
cartPriceDetails = cartPriceDetails!!,
availablePaymentMethods = if (selectedPaymentMethod!!.code == FREE_PAYMENT_KEY) emptyList() else availablePaymentMethods!!,
selectedPaymentMethod = selectedPaymentMethod!!,
selectedDeliveryDay = selectedDeliveryDay!!,
selectedDeliveryInterval = selectedDeliveryInterval!!,
user = userRepository.getUserFromCache()!!,
pointsRate = it.third
)
)
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
}).addTo(compositeDisposable)
}
}
private fun canProceedToPayment
():
Boolean =
if (isGuest
()) {
(selectedDeliveryAddress != null || selectedStore != null || guestTempAddress != null) && if (selectedShippingType == ShippingType.STORE) guestContactInfo != null
else selectedShippingMethod != null
} else selectedDeliveryAddress != null
private fun loadShippingDataForPickup() {
// selectedShippingType = ShippingType.STORE
// getAvailableStoresUseCase.execute()
// .compose(getIOTransformer())
// .doOnSubscribe {
// //SHOW LOADING
// handleEvent(CheckoutEvent.StartLoadingCheckoutData)
// }
// .subscribe({
// availableStores = it
// selectedStore = it.first()
// handleEvent(
// CheckoutEvent.OnShippingDataLoaded(
// availableDeliveryDays = availableDeliveryDays,
// availableAddresses = cachedUserAddresses,
// isGuest = isGuest(),
// selectedShippingType = ShippingType.STORE,
// selectedDeliveryAddress = selectedDeliveryAddress,
// selectedMethod = selectedShippingMethod,
// availableStores = availableStores,
// selectedStore = selectedStore,
// canProceedToPayment = canProceedToPayment(),
// cartPriceDetails = cartPriceDetails!!,
// electronicsCount = electronicsCount,
// groceriesCount = groceriesCount
// )
// )
// }, NetworkErrorHandlingImpl {
// handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
// }).addTo(compositeDisposable)
}
private fun isGuest() = !authenticationRepo.isUserLoggedIn()
private fun fetchPaymentMethods
(): Single
<List<AppAvailablePaymentMethod
>> {
return checkoutRepository.getAvailablePaymentMethods().map {
it.map(IAvailablePaymentMethod::toModel)
}
}
fun onPaymentMethodSelected(paymentMethod: AppAvailablePaymentMethod) {
setPaymentMethodOnCartUseCase.execute(
paymentMethod
).andThen(fetchCartPriceDetailsUseCase.execute()).compose(getIOTransformer())
.doOnSubscribe {
selectedPaymentMethod = paymentMethod
handleEvent(CheckoutEvent.OnPaymentMethodSelected(paymentMethod))
}.subscribe({
handleEvent(CheckoutEvent.OnCartPricesUpdated(it))
cartPriceDetails = it
}, NetworkErrorHandlingImpl {
simpleFeedbackLiveEvent.value = it
}).addTo(compositeDisposable)
selectedPaymentMethod = paymentMethod
}
//
// //SETS ADDRESS ON CART AND GET AVAILABLE DELIVERY METHODS
// //THEN SETS THE FIRST DELIVERY METHOD AS SELECTED
// private fun setDeliveryAddressAndGetAvailableMethods(): Single<AppAvailableDeliveryMethod> {
// // There are 2 cases when adding a shipping address:
// // 1. If there is a default address, send the default address id.
// // 2. If there is no default address, send the first address id.
// val address = cachedUserAddresses.find { address ->
// address.defaultShipping == true
// } ?: cachedUserAddresses.first()
// selectShippingAddress(address)
// return checkoutRepository.setBillingAddressAndGetDeliveryMethods(address.id)
// .map {
// it.map(IAvailableDeliveryMethod::toModel)
// }.doOnSuccess {
// availableShippingMethods = it
// }
// .flatMap {
// checkoutRepository.setShippingMethodOnCart(
// carrierCode = selectedShippingMethod?.carrierCode ?: it.first().carrierCode,
// methodCode = selectedShippingMethod?.methodCode ?: it.first().methodCode,
// ).andThen(Single.just(it.first()))
// }
// }
fun onASAPDeliverySelected() {
selectedDeliveryTime = DeliveryTime.ASAP
selectedDeliveryDay = availableDeliveryDays!!.first()
selectedDeliveryInterval = selectedDeliveryDay!!.intervals.first()
setDeliveryTimeUseCase.execute(
selectedDeliveryDay!!.date, selectedDeliveryInterval!!.id
).compose(getIOTransformer<Completable>()).doOnSubscribe {
handleEvent(
CheckoutEvent.OnDeliveryTimeSelected(
DeliveryTime.ASAP,
selectedDeliveryInterval = selectedDeliveryInterval!!,
selectedDeliveryDay = selectedDeliveryDay!!,
blockPlaceOrder = true
)
)
}.subscribe({
handleEvent(
CheckoutEvent.OnDeliveryTimeSelected(
DeliveryTime.ASAP,
selectedDeliveryInterval = selectedDeliveryInterval!!,
selectedDeliveryDay = selectedDeliveryDay!!,
blockPlaceOrder = false
)
)
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
}).addTo(compositeDisposable)
}
fun clearCart() {
ProductInfoCache.resetCartCount()
sessionService.removeCartId()
}
fun handleTransaction
(resultCode: Int, paymentId:
String?) {
when (resultCode) {
TRANSACTION_SUCCESSFUL, TRANSACTION_SUCCESSFUL_PARSING_ISSUE -> {
handleEvent(
CheckoutEvent.OnOrderPlacedSuccessfully(
listOf(paymentId!!)
)
)
}
TRANSACTION_REJECTED, TRANSACTION_REJECTED_PARSING_ISSUE -> {
handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR("تم رفض المعاملة")))
}
USER_CANCELED, USER_CANCELED_3D_SECURE_VERIFICATION, USER_CANCELED_3D_SECURE_VERIFICATION_PARSING_ISSUE -> {
handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR("تم إلغاء المعاملة")))
}
else -> {
handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR("حدث خطأ محهول")))
}
}
}
private fun proceedWithCreditCard() {
analyticsService.logPaymentMethodOptionSelection(selectedPaymentMethod!!.title)
proceedWithCreditCardUseCase.execute(selectedPaymentMethod!!.code)
.compose(getIOTransformer()).doOnSubscribe {
handleEvent(CheckoutEvent.OnPlaceOrderClicked)
}.subscribe({
handleEvent(CheckoutEvent.PlaceOrderWithCC(it))
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
}).addTo(compositeDisposable)
}
private fun proceedWithPayFort() {
analyticsService.logPaymentMethodOptionSelection(selectedPaymentMethod!!.title)
(checkoutRepository.setOrderSource())
.andThen(
getPayFortSdkTokenUseCase.execute()
)
.doOnSubscribe {
handleEvent(CheckoutEvent.OnPlaceOrderClicked)
}
.subscribe({
// prepare payment request
val fortRequest = FortRequest()
fortRequest.isShowResponsePage = true
fortRequest.requestMap = collectRequestMap(
it.first,
it.second,
sessionService.getCurrentUser(AppUser::class.java)!!.emailAddress,
cartPriceDetails!!.grandTotal.toFloat()
)
// to [display/use] the SDK response page
simpleFeedbackLiveEvent.value =
CheckoutClickActions.ProceedWithPayFortPayment(
fortRequest
)
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
})
.addTo(compositeDisposable)
}
private fun proceedWithPaymob() {
analyticsService.logPaymentMethodOptionSelection(selectedPaymentMethod!!.title)
setOrderSourceOnCartUseCase.execute()
.andThen(getPaymobIframeUrlOrReferenceNumberUseCase.execute())
.compose(getIOTransformer()).doOnSubscribe {
handleEvent(CheckoutEvent.OnPlaceOrderClicked)
}.subscribe({
handleEvent(CheckoutEvent.OnPendingGateway)
simpleFeedbackLiveEvent.value = CheckoutClickActions.ProceedWithPaymobPayment(
iFrameUrl = if (selectedPaymentMethod!!.code != PaymentMethod.PAYMOB_KIOSK.code) it else null,
referenceNumber = if (selectedPaymentMethod!!.code == PaymentMethod.PAYMOB_KIOSK.code) it else null
)
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
}).addTo(compositeDisposable)
}
fun proceedWithCash() {
analyticsService.logPaymentMethodOptionSelection(selectedPaymentMethod!!.title)
setOrderSourceOnCartUseCase.execute().andThen(
proceedWithCashUseCase.execute()
).doOnSuccess {
clearCart()
}.
compose(getIOTransformer
<List<String>>()).
doOnSubscribe {
handleEvent(CheckoutEvent.OnPlaceOrderClicked)
}.subscribe({
handleEvent(CheckoutEvent.OnOrderPlacedSuccessfully(it))
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
}).addTo(compositeDisposable)
}
fun placeOrder() {
val minimumOrderAmount = getMinimumOrderAmount()
if (cartPriceDetails!!.subtotalWithDiscountExcludingTax < minimumOrderAmount) {
showSideEffect(CheckoutSideEffect.ShowMinimumOrderAmountError(minimumOrderAmount))
} else {
// if (selectedDeliveryAddress?.phoneVerified == true) {
if (selectedPaymentMethod?.code?.contains("accept") == true) {
proceedWithPaymob()
} else if (selectedPaymentMethod?.code?.contains("fort") == true) {
proceedWithPayFort()
} else {
proceedWithCash()
}
// } else {
// simpleFeedbackLiveEvent.value =
// CheckoutClickActions.VerifyAddress(selectedDeliveryAddress!!)
// }
}
}
fun onAddressSelected(shippingAddress: UIDeliveryAddress) {
fetchDeliveryDatesUseCase.execute(
selectedDeliveryAddress!!.city!!.code,
selectedDeliveryAddress!!.district!!.id.toString()
).flatMap {
availableDeliveryDays = it
if (it.isNotEmpty()) {
selectedDeliveryDay = it.first()
availableDeliveryIntervals = selectedDeliveryDay!!.intervals
selectedDeliveryInterval = availableDeliveryIntervals!!.first()
setDeliveryTimeUseCase.execute(
selectedDeliveryDay!!.date, selectedDeliveryInterval!!.id
).andThen(Single.just(it))
} else Single.just(it)
}.compose(getIOTransformer()).doOnSubscribe {
selectedDeliveryAddress = shippingAddress
handleEvent(CheckoutEvent.OnAddressSelected(shippingAddress))
}.subscribe({
handleEvent(
CheckoutEvent.OnDeliveryDaysUpdated(
it,
selectedDeliveryDay = selectedDeliveryDay!!,
selectedDeliveryInterval = selectedDeliveryInterval!!
)
)
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnPlaceOrderFailed(it))
}).addTo(compositeDisposable)
}
fun onAddressVerified() {
val location = getDefaultUserLocationUseCase.execute()
getUserAddressesUseCase.execute().zipWith(getAvailableRegionsUseCase.execute(), ::Pair)
.doOnSuccess {
cachedUserAddresses = it.first.mapIndexed { index, address ->
val isInDeliveryZone = isAddressSupportedByStoreCode(
address = address,
storeCode = location?.availableStore?.storeCode!!,
it.second
)
UIDeliveryAddress(
isInDeliveryZone = isInDeliveryZone,
id = address.id,
addressName = if (address.addressName != null && address.addressName!!.isNotEmpty()) address.addressName else "Address #${index + 1}",
district = address.district,
city = address.city,
region = address.region,
apartment = address.apartment,
lastName = address.lastName,
firstName = address.firstName,
defaultShipping = address.defaultShipping,
floor = address.floor,
defaultBilling = address.defaultBilling,
building = address.building,
countryCode = address.countryCode,
customerNotes = address.customerNotes,
street = address.street,
phone = address.phone,
lat = address.lat,
lng = address.lng,
compound = address.compound,
)
}
selectedDeliveryAddress =
cachedUserAddresses.find { it.id == selectedDeliveryAddress?.id }
}.compose(getIOTransformer()).doOnSubscribe {
handleEvent(CheckoutEvent.StartLoadingCheckoutData)
}.subscribe({}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
}).addTo(compositeDisposable)
}
fun onAddressCreated(shippingAddress: AppShippingAddress) {
val location = getDefaultUserLocationUseCase.execute()
getAvailableRegionsUseCase.execute().compose(getIOTransformer()).subscribe({
loadShippingDataForDelivery(shippingAddress.let { address ->
val isInDeliveryZone = isAddressSupportedByStoreCode(
address = address, storeCode = location?.availableStore?.storeCode!!, it
)
UIDeliveryAddress(
isInDeliveryZone = isInDeliveryZone,
id = address.id,
addressName = if (address.addressName != null && address.addressName!!.isNotEmpty()) address.addressName else "",
district = address.district,
city = address.city,
region = address.region,
apartment = address.apartment,
lastName = address.lastName,
firstName = address.firstName,
defaultShipping = address.defaultShipping,
floor = address.floor,
defaultBilling = address.defaultBilling,
building = address.building,
countryCode = address.countryCode,
customerNotes = address.customerNotes,
street = address.street,
phone = address.phone,
lat = address.lat,
lng = address.lng,
compound = address.compound,
)
})
}, {
}).addTo(compositeDisposable)
}
fun applyCouponToCart
(couponCode:
String) {
checkoutRepository.applyCouponToCart(couponCode).compose(getIOTransformer()).doOnSubscribe {
_promoCodeState.value = PromoCodeState.Applying
handleEvent(CheckoutEvent.OnCouponStateChange)
}.subscribe({
_promoCodeState.value = PromoCodeState.Showing(it.appliedCouponCode!!)
handleEvent(CheckoutEvent.OnCartPricesUpdated(it))
}, NetworkErrorHandlingImpl {
if (it is AppError.MagentoError && it.magentoThrowable.getWhichErrors() == Errors.WRONG_COUPON) _promoCodeState.value =
PromoCodeState.InvalidPromoCode
else if (it is AppError.MagentoError && it.magentoThrowable.getWhichErrors() == Errors.COUPON_ERROR) _promoCodeState.value =
PromoCodeState.AlreadyAppliedAnotherPromoCode
else {
_promoCodeState.value = PromoCodeState.Input
simpleFeedbackLiveEvent.value = it
}
}).addTo(compositeDisposable)
}
fun removeCouponFromCart() {
checkoutRepository.removeCouponFromCart().compose(getIOTransformer()).doOnSubscribe {
handleEvent(CheckoutEvent.OnCouponStateChange)
}.subscribe({
handleEvent(CheckoutEvent.OnCartPricesUpdated(it))
_priceDetails.value = it
_promoCodeState.value = PromoCodeState.Input
}, NetworkErrorHandlingImpl {
simpleFeedbackLiveEvent.value = it
if (priceDetails.value?.appliedCouponCode != null) _promoCodeState.value =
PromoCodeState.Showing(priceDetails.value?.appliedCouponCode!!)
else _promoCodeState.value = PromoCodeState.Input
}).addTo(compositeDisposable)
}
fun onFailedToInitializeOpaySdk
(exception:
Exception) {
loggingService.e("Checkout Fragment", "opay Exception during payment")
loggingService.stackTrack(exception)
handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(exception.message)))
}
fun onSuccess(
) {
//VERY IMPORTANT TO CLEAR CART FROM ANY CACHED COUNTS
clearCart()
handleEvent(
CheckoutEvent.OnOrderPlacedSuccessfully(
orderNumbers
)
)
}
fun onFailure
(message:
String) {
handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(message)))
}
fun proceedToPayment
(note:
String?) {
// if (selectedShippingType == ShippingType.STORE)
// setPickupFromStoreUseCase.execute(selectedStore!!, guestContactInfo)
// .zipWith(getAvailablePaymentMethodsUseCase.execute()) { shippingMethod, paymentMethods ->
// Pair(shippingMethod, paymentMethods)
// }
// .flatMap { result ->
// selectedPaymentMethod = result.second.first()
// setPaymentMethodOnCartUseCase.execute(result.second.first())
// .andThen(fetchCartPriceDetailsUseCase.execute()).flatMap {
// cartPriceDetails = it
// addNoteToCartUseCase.execute(note).andThen(
// Single.just(result)
// )
// }
// }
// .compose(getIOTransformer())
// .doOnSubscribe {
// handleEvent(CheckoutEvent.OnProceedToPayment)
// }
// .subscribe({
// selectedShippingMethod = it.first
// availablePaymentMethods = it.second
// handleEvent(
// CheckoutEvent.OnPaymentMethodsLoaded(
// it.second,
// selectedPaymentMethod!!,
// selectedShippingMethod!!,
// cartPriceDetails!!
// )
// )
// }, NetworkErrorHandlingImpl {
// handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
// }).addTo(compositeDisposable)
// else {
// getAvailablePaymentMethodsUseCase.execute()
// .flatMap { list ->
// selectedPaymentMethod = list.first()
// setPaymentMethodOnCartUseCase.execute(list.first())
// .andThen(fetchCartPriceDetailsUseCase.execute()).flatMap {
// cartPriceDetails = it
// addNoteToCartUseCase.execute(note).andThen(
// Single.just(list)
// )
// }
// }
// .compose(getIOTransformer())
// .doOnSubscribe {
// handleEvent(CheckoutEvent.OnProceedToPayment)
// }
// .subscribe({
// availablePaymentMethods = it
// handleEvent(
// CheckoutEvent.OnPaymentMethodsLoaded(
// it,
// selectedPaymentMethod!!,
// selectedShippingMethod!!,
// cartPriceDetails!!
// )
// )
// }, NetworkErrorHandlingImpl {
// handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
// }).addTo(compositeDisposable)
// }
}
fun onShippingMethodSelected(shippingType: ShippingType) {
if (shippingType == ShippingType.HOME) {
loadShippingDataForDelivery()
} else {
loadShippingDataForPickup()
}
}
fun onStoreSelected(store: AppShippingLocation) {
selectedStore = store
}
fun editShippingDetails() {
handleEvent(CheckoutEvent.GoBackToShipping)
}
fun setSelectedDeliverySlot(time: AppDeliveryInterval?, day: AppDeliveryDay?) {
if (time != null && day != null) {
selectedDeliveryInterval = time
selectedDeliveryDay = day
selectedDeliveryTime = DeliveryTime.SCHEDULE
setDeliveryTimeUseCase.execute(
selectedDeliveryDay!!.date, selectedDeliveryInterval!!.id
).compose(getIOTransformer<Completable>()).doOnSubscribe {
handleEvent(
CheckoutEvent.OnDeliveryTimeSelected(
DeliveryTime.SCHEDULE,
selectedDeliveryInterval = selectedDeliveryInterval!!,
selectedDeliveryDay = selectedDeliveryDay!!,
blockPlaceOrder = true
)
)
}.subscribe({
handleEvent(
CheckoutEvent.OnDeliveryTimeSelected(
DeliveryTime.SCHEDULE,
selectedDeliveryInterval = selectedDeliveryInterval!!,
selectedDeliveryDay = selectedDeliveryDay!!,
blockPlaceOrder = false
)
)
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnFailedToLoadCheckoutData(it))
}).addTo(compositeDisposable)
}
}
fun toggleDeliverySection
(expanded:
Boolean) {
handleEvent(CheckoutEvent.OnToggleDeliveryExpanded(expanded))
}
fun togglePaymentSection
(expanded:
Boolean) {
handleEvent(CheckoutEvent.OnTogglePaymentExpanded(expanded))
}
private fun isAddressSupportedByStoreCode(
address: AppShippingAddress, storeCode:
String, regions:
List<AppRegion
>
) =
regions.find { it.id == address.region?.id }?.cities?.find { it.id == address.city?.id }?.districts?.find { it.id == address.district?.id }?.store?.storeCode == storeCode
fun refreshScreen() {
if (_stateLiveData.value == CheckoutState.LoadingCheckoutData) return else loadShippingDataForDelivery()
}
fun applyLoyaltyPoints(points: Int) {
handleEvent(CheckoutEvent.OnApplyLoyaltyPoints)
applyLoyaltyPointsUseCase.execute(points)
.flatMap({ userRepository.getUserFromNetwork() }, { cartPrices, user ->
Pair(cartPrices, user)
}).compose(getIOTransformer()).subscribe({
handleEvent(
CheckoutEvent.OnApplyLoyaltyPointsSuccessfully(it.first, it.second)
)
}, NetworkErrorHandlingImpl {
handleEvent(CheckoutEvent.OnApplyLoyaltyPointsFailed(it))
}).addTo(compositeDisposable)
}
private fun getMinimumOrderAmount(): Int = storeConfigRepo.getMinimumOrderAmount()
override fun onCancel(
) {
loggingService.d("Cancelled ", responseMap.toString());
loggingService.d("FortSDK failure", responseMap.toString())
handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(null)))
}
override fun onSuccess(
) {
loggingService.i("Success ", fortResponseMap.toString())
//VERY IMPORTANT TO CLEAR CART FROM ANY CACHED COUNTS
clearCart()
handleEvent(
CheckoutEvent.OnOrderPlacedSuccessfully(
(fortResponseMap
?.
get("merchant_reference") as
String).
split("_")
)
)
}
override fun onFailure(
) {
loggingService.e("Failure ", fortResponseMap.toString())
loggingService.d("FortSDK failure", fortResponseMap.toString())
handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(null)))
}
fun onFailedToInitializePayFortSdk
(exception:
Exception) {
loggingService.e("Checkout Fragment", "PayFort Exception during payment")
loggingService.stackTrack(exception)
handleEvent(CheckoutEvent.OnPlaceOrderFailed(AppError.UNKNOWN_ERROR(exception.message)))
}
//PAYFORT SDK REQUEST
private fun collectRequestMap(
requestMap["command"] = "AUTHORIZATION"
requestMap["customer_email"] = email
requestMap["currency"] = "EGP"
requestMap["amount"] = (total * 100).toInt().toString()
requestMap["language"] = LocaleManager.getLocale()
// requestMap["return_url"] = payfortSignatureMutation.return_url!!
requestMap["merchant_reference"] = merchantRef
// requestMap["merchant_identifier"] = payfortSignatureMutation.merchant_identifier!!
// requestMap["signature"] = payfortSignatureMutation.signature!!
requestMap["sdk_token"] = sdkToken
// requestMap["access_code"] = payfortSignatureMutation.access_code!!
return requestMap
}
}