Skip to content

Commit

Permalink
MBL-1919: API | Implement BuildPaymentPlan Query On Checkout Screen (#…
Browse files Browse the repository at this point in the history
…2190)

* add build payment plan query and models

* linter

* fix tests

* remove test network call

* remove unused imports

* remove test compose view

* formatting

* hook up build payment plan query to checkout

* linter

* add and modify tests

* linter

* Minor correction

---------

Co-authored-by: Leigh Douglas <[email protected]>
  • Loading branch information
leighdouglas and Leigh Douglas authored Dec 20, 2024
1 parent 95476a9 commit 802bba4
Show file tree
Hide file tree
Showing 24 changed files with 846 additions and 171 deletions.
128 changes: 85 additions & 43 deletions app/src/main/graphql/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ type Query {
"""
Fetches an order given its id.
"""
order(id: ID!): Order
order(id: ID!): Order!

photoForEditor(id: ID!): Photo

Expand Down Expand Up @@ -796,9 +796,11 @@ enum Feature {

multiple_shipfrom_locations_2024

separate_payment_section
pledge_redemption_unified_creator_ui

react_backed_projects

copy_addons
}

"""
Expand Down Expand Up @@ -986,6 +988,11 @@ type CreditCard {
"""
lastFour: String!

"""
The card type, but in lowercase.
"""
lowercaseType: LowercaseCreditCardTypes!

"""
The card's payment type.
"""
Expand Down Expand Up @@ -1031,6 +1038,25 @@ enum CreditCardTypes {
UNIONPAY
}

"""
Credit card types.
"""
enum LowercaseCreditCardTypes {
amex

discover

jcb

mastercard

visa

diners

unionpay
}

"""
Credit card payment types.
"""
Expand Down Expand Up @@ -3099,11 +3125,6 @@ type Cart {
"""
projectQuestions: [CartQuestion!]!

"""
Whether the cart could be finalized or is missing required info
"""
readyToBeFinalized: Boolean!

"""
Whether or not this cart needs an address to be finalized
"""
Expand Down Expand Up @@ -4516,6 +4537,16 @@ type Order implements Node {
"""
cart: Cart

"""
Whether there is required action for the backer to take in their pledge manager
"""
checkoutRequired: Boolean!

"""
The state of checkout (taking into account order and cart status)
"""
checkoutState: CheckoutStateEnum!

"""
Whether or not the order would be auto-exemptable if the order had no cross-sells.
"""
Expand All @@ -4536,6 +4567,11 @@ type Order implements Node {
"""
finalized: Boolean!

"""
Whether or not the order was finalized but non-exempt.
"""
finalizedAndNotExempt: Boolean!

id: ID!

"""
Expand All @@ -4554,9 +4590,9 @@ type Order implements Node {
project: Project!

"""
The cost of shipping
The cost of shipping, formatted
"""
shippingAmount: Int
shippingAmountFormatted: String

"""
The order's state, e.g. draft, collected, dropped, etc.
Expand All @@ -4569,9 +4605,14 @@ type Order implements Node {
total: Int

"""
The total tax amount for the order
The total cost for the order including taxes and shipping, formatted
"""
totalFormatted: String

"""
The total tax amount for the order, formatted
"""
totalTax: Int
totalTaxFormatted: String
}

"""
Expand Down Expand Up @@ -4608,66 +4649,72 @@ enum OrderStateEnum {
An add-on reward included in an Order during Pledge Redemption as a Cross Sell.
"""
type OrderCrossSell {
"""
The content type of the reward
"""
contentsType: String!
id: ID!

"""
Description of the add-on
The orderable of the cross sell
"""
description: String!

id: ID!
orderable: Orderable!

"""
The add-on name.
The quantity of the add-on in the Order
"""
name: String
quantity: Int!

"""
Configuration for the add-on
The total price of the cross-sell
"""
orderableConfig: OrderableConfig!
totalPriceFormatted: String!

"""
The ID of the orderable
The price of one unit of the cross-sell
"""
orderableId: String!
unitPriceFormatted: String!
}

union Orderable = Reward

"""
The state of checkout, e.g. complete, in progress, incomplete.
"""
enum CheckoutStateEnum {
"""
The quantity of the add-on in the Order
complete
"""
quantity: Int!
complete

"""
The shipping preference of the reward
in_progress
"""
shippingPreference: String!
in_progress

"""
Shipping rates for all shippable countries,
including those that are children of superregions
not_started
"""
shippingRatesExpanded: [ShippingRate!]!
not_started
}

type PaymentIncrement {
amount: Money!

id: ID!
id: ID

paymentIncrementableId: ID!
paymentIncrementableId: ID

paymentIncrementableType: String!
paymentIncrementableType: String

scheduledCollection: DateTime!
scheduledCollection: ISO8601DateTime!

state: String!

stateReason: String
}

"""
An ISO 8601-encoded datetime
"""
scalar ISO8601DateTime

"""
An adjustment summary.
"""
Expand Down Expand Up @@ -4760,11 +4807,6 @@ enum AdjustmentStatusType {
CANCELLED
}

"""
An ISO 8601-encoded datetime
"""
scalar ISO8601DateTime

"""
Metadata for adjustment to order
"""
Expand Down Expand Up @@ -16273,12 +16315,12 @@ input ConfirmOrderAddressInput {
Autogenerated return type of CreateWave
"""
type CreateWavePayload {
checkoutWave: CheckoutWave!

"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String

project: Project!
}

"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import com.kickstarter.features.pledgeredemption.viewmodels.PledgeRedemptionViewModel
import com.kickstarter.libs.featureflag.FlagKey
import com.kickstarter.libs.utils.extensions.getEnvironment
import com.kickstarter.libs.utils.extensions.isDarkModeEnabled
import com.kickstarter.mock.factories.RewardFactory
Expand Down Expand Up @@ -65,8 +64,6 @@ class PledgeRedemptionActivity : ComponentActivity() {
newPaymentMethodClicked = { },
onDisclaimerItemClicked = {},
onAccountabilityLinkClicked = {},
isPlotEnabled = env.featureFlagClient()
?.getBoolean(FlagKey.ANDROID_PLEDGE_OVER_TIME) ?: false,
)
}
}
Expand Down
29 changes: 29 additions & 0 deletions app/src/main/java/com/kickstarter/libs/KSCurrency.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,35 @@ class KSCurrency(private val currentConfig: CurrentConfigTypeV2) {
return NumberUtils.format(currencyOptions.value() ?: 0F, numberOptions).trimAllWhitespace()
}

/**
* Returns a currency string appropriate to the user's locale and location relative to a project.
*
* @param initialValue Value to display, local to the project's currency.
* @param projectCurrency The project currency.
* @param projectCurrenctCurrency The project current currency.
* @param excludeCurrencyCode If true, hide the US currency code for US users only.
*/
@JvmOverloads
fun format(
initialValue: Double,
projectCurrency: String?,
projectCurrentCurrency: String?,
excludeCurrencyCode: Boolean = true,
roundingMode: RoundingMode = RoundingMode.DOWN,
currentCurrency: Boolean = false
): String {
val country = (if (currentCurrency) projectCurrentCurrency else projectCurrency)?.let { findByCurrencyCode(it) } ?: return ""
val roundedValue = getRoundedValue(initialValue, roundingMode)
val currencyOptions = currencyOptions(roundedValue, country, excludeCurrencyCode)
val numberOptions = NumberOptions.builder()
.currencyCode(currencyOptions.currencyCode())
.currencySymbol(currencyOptions.currencySymbol())
.roundingMode(roundingMode)
.precision(NumberUtils.precision(initialValue, roundingMode))
.build()
return NumberUtils.format(currencyOptions.value() ?: 0F, numberOptions).trimAllWhitespace()
}

/**
* Returns a currency string appropriate to the user's locale and preferred currency.
*
Expand Down
25 changes: 25 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 @@ -88,6 +88,31 @@ object RewardViewUtils {
return spannableString
}

/**
* Returns a SpannableString representing currency that shrinks currency symbol if it's necessary.
* Special case: US people looking at US currency just get the currency symbol.
*
*/
fun styleCurrency(value: Double, projectCurrency: String?, projectCurrentCurrency: String?, ksCurrency: KSCurrency): SpannableString {
val formattedCurrency = ksCurrency.format(initialValue = value, projectCurrency = projectCurrency, projectCurrentCurrency = projectCurrentCurrency, roundingMode = RoundingMode.HALF_UP)
val spannableString = SpannableString(formattedCurrency)

val country = projectCurrency?.let {
Country.findByCurrencyCode(it) ?: return spannableString
} ?: return spannableString

val currencyNeedsCode = ksCurrency.currencyNeedsCode(country, true)
val currencySymbolToDisplay = ksCurrency.getCurrencySymbol(country, true).trimAllWhitespace()

if (currencyNeedsCode) {
val startOfSymbol = formattedCurrency.indexOf(currencySymbolToDisplay)
val endOfSymbol = startOfSymbol + currencySymbolToDisplay.length
spannableString.setSpan(RelativeSizeSpan(.7f), startOfSymbol, endOfSymbol, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
}

return spannableString
}

/**
* Returns a String representing currency based on given currency code and symbol ex. $12 USD
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,16 @@ fun String.format(key1: String, value1: String?): String {
}
return this.replace(substitutions)
}

fun String.format(key1: String, value1: String?, key2: String, value2: String?): String {
val substitutions: HashMap<String, String?> = object : HashMap<String, String?>() {
init {
put(key1, value1)
put(key2, value2)
}
}
return this.replace(substitutions)
}
fun String.replace(substitutions: Map<String, String?>): String {
val builder = StringBuilder()
for (key in substitutions.keys) {
Expand Down
Loading

0 comments on commit 802bba4

Please sign in to comment.