Skip to content

Commit

Permalink
MBL-1567: Pledge redemption (Backer Shipping Expectation & Backer Shi…
Browse files Browse the repository at this point in the history
  • Loading branch information
Arkariang authored Aug 28, 2024
1 parent 25bf865 commit bd46335
Show file tree
Hide file tree
Showing 71 changed files with 5,789 additions and 5,825 deletions.
2 changes: 1 addition & 1 deletion app/internal_version_code.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2014150908
2014150913
2 changes: 1 addition & 1 deletion app/internal_version_name.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.23.0
3.24.0
14 changes: 14 additions & 0 deletions app/src/main/graphql/fragments.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,21 @@ fragment reward on Reward {
convertedAmount{
... amount
}
shippingRules {
... shippingRule
}
shippingPreference
remainingQuantity
limit
limitPerBacker
startsAt
endsAt
rewardType
allowedAddons {
nodes {
id
}
}
localReceiptLocation {
... location
}
Expand Down Expand Up @@ -360,6 +368,12 @@ fragment shippingRule on ShippingRule {
location {
... location
}
estimatedMin {
amount
}
estimatedMax {
amount
}
}


Expand Down
22 changes: 22 additions & 0 deletions app/src/main/java/com/kickstarter/libs/KSCurrency.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,33 @@ package com.kickstarter.libs
import com.kickstarter.libs.models.Country
import com.kickstarter.libs.models.Country.Companion.findByCurrencyCode
import com.kickstarter.libs.utils.NumberUtils
import com.kickstarter.libs.utils.ProjectViewUtils
import com.kickstarter.libs.utils.extensions.trimAllWhitespace
import com.kickstarter.models.Project
import java.math.RoundingMode
import kotlin.jvm.JvmOverloads

/**
* Currency symbol, which can be positioned at start or end of amount depending on country
*/
fun KSCurrency?.getCurrencySymbols(project: Project): Pair<String, String> {
val currencySymbolStartAndEnd = this?.let {
val symbolAndStart = ProjectViewUtils.currencySymbolAndPosition(
project,
this
)
val symbol = symbolAndStart.first
val symbolAtStart = symbolAndStart.second
if (symbolAtStart) {
Pair(symbol.toString(), "")
} else {
Pair("", symbol.toString())
}
} ?: Pair("", "")

return currencySymbolStartAndEnd
}

class KSCurrency(private val currentConfig: CurrentConfigType) {
/**
* Returns a currency string appropriate to the user's locale and location relative to a project.
Expand Down
32 changes: 32 additions & 0 deletions app/src/main/java/com/kickstarter/libs/utils/RewardUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import android.util.Pair
import com.kickstarter.R
import com.kickstarter.libs.KSString
import com.kickstarter.libs.models.Country
import com.kickstarter.libs.utils.extensions.isNonZero
import com.kickstarter.models.Project
import com.kickstarter.models.Reward
Expand All @@ -14,6 +15,12 @@ import kotlin.math.max

object RewardUtils {

fun minPledgeAmount(reward: Reward, project: Project): Double {
return Country.findByCurrencyCode(project.currency())?.minPledge?.toDouble() ?: 0.0
}

fun maxPledgeAmount(reward: Reward, project: Project): Double = Country.findByCurrencyCode(project.currency())?.maxPledge?.toDouble() ?: 0.0

/**
* Returns `true` if the reward has backers, `false` otherwise.
*/
Expand Down Expand Up @@ -66,6 +73,10 @@ object RewardUtils {
return rewardsItems != null && rewardsItems.isNotEmpty()
}

fun shipsWorldwide(reward: Reward): Boolean = reward.shippingPreference().equals(Reward.ShippingPreference.UNRESTRICTED.name, ignoreCase = true)

fun shipsToRestrictedLocations(reward: Reward): Boolean = reward.shippingPreference().equals(Reward.ShippingPreference.RESTRICTED.name, ignoreCase = true)

/**
* Returns `true` if the reward has a limit set, and the limit has not been reached, `false` otherwise.
*/
Expand Down Expand Up @@ -221,4 +232,25 @@ object RewardUtils {
fun getFinalBonusSupportAmount(addedBonusSupport: Double, initialBonusSupport: Double): Double {
return if (addedBonusSupport > 0) addedBonusSupport else initialBonusSupport
}

/** For the checkout we need to send a list repeating as much addOns items
* as the user has selected:
* User selection [R, 2xa, 3xb]
* Checkout data [R, a, a, b, b, b]
*/
fun extendAddOns(flattenedList: List<Reward>): List<Reward> {
val mutableList = mutableListOf<Reward>()

flattenedList.map {
if (!it.isAddOn()) mutableList.add(it)
else {
val q = it.quantity() ?: 1
for (i in 1..q) {
mutableList.add(it)
}
}
}

return mutableList.toList()
}
}
109 changes: 109 additions & 0 deletions app/src/main/java/com/kickstarter/libs/utils/RewardViewUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import com.kickstarter.libs.KSCurrency
import com.kickstarter.libs.KSString
import com.kickstarter.libs.models.Country
import com.kickstarter.libs.utils.extensions.isBacked
import com.kickstarter.libs.utils.extensions.isNull
import com.kickstarter.libs.utils.extensions.trimAllWhitespace
import com.kickstarter.models.Project
import com.kickstarter.models.Reward
import com.kickstarter.models.ShippingRule
import java.math.RoundingMode

object RewardViewUtils {
Expand Down Expand Up @@ -139,4 +141,111 @@ object RewardViewUtils {
maxInputAmountWithCurrency
) ?: ""
}

/**
* Return the string for the estimated shipping costs for a given shipping rule
*
* Ex. "About $10-$15" or "About $10-%15 each"
*/
fun getEstimatedShippingCostString(
context: Context,
ksCurrency: KSCurrency,
ksString: KSString,
project: Project,
rewards: List<Reward> = listOf(),
selectedShippingRule: ShippingRule,
multipleQuantitiesAllowed: Boolean,
useUserPreference: Boolean,
useAbout: Boolean
): String {
var min = ""
var max = ""
var minTotal = 0.0
var maxtotal = 0.0
rewards.forEach { reward ->
if (!RewardUtils.isDigital(reward) && RewardUtils.shipsToRestrictedLocations(reward) && !RewardUtils.isLocalPickup(reward)) {
reward.shippingRules()?.filter {
it.location()?.id() == selectedShippingRule.location()?.id()
}?.map {
minTotal += (it.estimatedMin() * (reward.quantity() ?: 1))
maxtotal += (it.estimatedMax() * (reward.quantity() ?: 1))
}
}

if (RewardUtils.shipsWorldwide(reward) && !reward.shippingRules().isNullOrEmpty()) {
reward.shippingRules()?.first()?.let {
minTotal += (it.estimatedMin() * (reward.quantity() ?: 1))
maxtotal += (it.estimatedMax() * (reward.quantity() ?: 1))
}
}
}
if (minTotal <= 0 || maxtotal <= 0) return ""

min = if (useUserPreference) {
ksCurrency.formatWithUserPreference(minTotal, project, RoundingMode.HALF_UP, precision = 2)
} else {
ksCurrency.format(minTotal, project, RoundingMode.HALF_UP)
}
max = if (useUserPreference) {
ksCurrency.formatWithUserPreference(maxtotal, project, RoundingMode.HALF_UP, precision = 2)
} else {
ksCurrency.format(maxtotal, project, RoundingMode.HALF_UP)
}

if (min.isEmpty() || max.isEmpty()) return ""

// TODO: Replace with defined string
val minToMaxString = if (multipleQuantitiesAllowed) "$min-$max each" else "$min-$max"

return if (useAbout) {
ksString.format(
context.getString(R.string.About_reward_amount),
"reward_amount",
minToMaxString
)
} else {
minToMaxString
}
}

/**
* Returns a string for the shipping costs for add-on cards
*
* Ex. " + $5 each"
*/
fun getAddOnShippingAmountString(
context: Context,
project: Project,
reward: Reward,
rewardShippingRules: List<ShippingRule>?,
ksCurrency: KSCurrency?,
ksString: KSString?,
selectedShippingRule: ShippingRule
): String {
if (rewardShippingRules.isNullOrEmpty() || ksCurrency.isNull() || ksString.isNull()) return ""
val shippingAmount =
if (!RewardUtils.isDigital(reward) && RewardUtils.isShippable(reward) && !RewardUtils.isLocalPickup(reward)) {
var cost = 0.0
rewardShippingRules.filter {
it.location()?.id() == selectedShippingRule.location()?.id()
}.map {
cost += it.cost()
}
if (cost > 0) ksCurrency?.format(cost, project)
else ""
} else {
""
}
if (shippingAmount.isNullOrEmpty()) return ""
val rewardAndShippingString =
context.getString(R.string.reward_amount_plus_shipping_cost_each)
val stringSections = rewardAndShippingString.split("+")
val shippingString = " +" + stringSections[1]
val ammountAndShippingString = ksString?.format(
shippingString,
"shipping_cost",
shippingAmount
)
return ammountAndShippingString ?: ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.kickstarter.libs.Config
import com.kickstarter.libs.preferences.StringPreferenceType
import com.kickstarter.models.ShippingRule
import org.json.JSONArray

/**
Expand Down Expand Up @@ -71,7 +72,6 @@ fun Config.syncUserFeatureFlagsFromPref(featuresFlagPreference: StringPreference
/**
* set the saved feature flags in to config feature object
*/

fun Config.setUserFeatureFlagsPrefWithFeatureFlag(
featuresFlagPreference: StringPreferenceType?,
featureName: String,
Expand All @@ -96,3 +96,30 @@ fun Config.setUserFeatureFlagsPrefWithFeatureFlag(
}?.toMap()
).build()
}

/**
* From a selected list of shipping rules, select the one that matches the config location
* if none matches return the first one.
* Config countryCode is based on IP location,
* example: if your network is within Canada, it will return Canada
* example: if your network is within Canada, but the given shipping Rules does not include
* Canada, it will return the first rule given in tha shipping rules list.
*/
fun Config.getDefaultLocationFrom(shippingRules: List<ShippingRule>): ShippingRule {
return if (shippingRules.isNotEmpty()) {
shippingRules.firstOrNull { it.location()?.country() == this.countryCode() }
?: shippingRules.first()
} else {
ShippingRule.builder().build()
}
// val location = Location.builder()
// .id(23424814)
// .country("FK")
// .displayableName("Falkland Islands")
// .name("Falkland Islands")
// .build()
// return ShippingRule.builder()
// .id(23424814)
// .location(location)
// .build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import androidx.fragment.app.Fragment
import com.kickstarter.ui.ArgumentsKey
import com.kickstarter.ui.data.PledgeData
import com.kickstarter.ui.data.PledgeReason
import com.kickstarter.ui.fragments.CrowdfundCheckoutFragment
import com.kickstarter.ui.fragments.PledgeFragment

fun Fragment.selectPledgeFragment(
pledgeData: PledgeData,
pledgeReason: PledgeReason
): Fragment {
return PledgeFragment().withData(pledgeData, pledgeReason)
val fragment = if (pledgeReason == PledgeReason.FIX_PLEDGE) {
PledgeFragment()
} else CrowdfundCheckoutFragment()
return fragment.withData(pledgeData, pledgeReason)
}

fun Fragment.withData(pledgeData: PledgeData?, pledgeReason: PledgeReason?): Fragment {
Expand Down
Loading

0 comments on commit bd46335

Please sign in to comment.