Skip to content

Commit d1751be

Browse files
Merge pull request #12766 from woocommerce/issue/12752-subscriptions-meta-1
[Products] Extract subscriptions outside of the Product DB table - Part 1
2 parents 06fce27 + 57e9381 commit d1751be

16 files changed

+820
-693
lines changed

WooCommerce/src/main/kotlin/com/woocommerce/android/model/Product.kt

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ data class Product(
8383
override val width: Float,
8484
override val height: Float,
8585
override val weight: Float,
86-
val subscription: SubscriptionDetails?,
8786
val isSampleProduct: Boolean,
8887
val specialStockStatus: ProductStockStatus? = null,
8988
val isConfigurable: Boolean = false,
@@ -153,7 +152,6 @@ data class Product(
153152
downloadExpiry == product.downloadExpiry &&
154153
isDownloadable == product.isDownloadable &&
155154
attributes == product.attributes &&
156-
subscription == product.subscription &&
157155
specialStockStatus == product.specialStockStatus &&
158156
minAllowedQuantity == product.minAllowedQuantity &&
159157
maxAllowedQuantity == product.maxAllowedQuantity &&
@@ -169,8 +167,7 @@ data class Product(
169167
get() {
170168
return weight > 0 ||
171169
length > 0 || width > 0 || height > 0 ||
172-
shippingClass.isNotEmpty() ||
173-
subscription?.oneTimeShipping == true
170+
shippingClass.isNotEmpty()
174171
}
175172
val productType get() = ProductType.fromString(type)
176173
val variationEnabledAttributes
@@ -334,7 +331,6 @@ data class Product(
334331
downloads = updatedProduct.downloads,
335332
downloadLimit = updatedProduct.downloadLimit,
336333
downloadExpiry = updatedProduct.downloadExpiry,
337-
subscription = updatedProduct.subscription,
338334
specialStockStatus = specialStockStatus,
339335
minAllowedQuantity = updatedProduct.minAllowedQuantity,
340336
maxAllowedQuantity = updatedProduct.maxAllowedQuantity,
@@ -482,20 +478,10 @@ fun Product.toDataModel(storedProductModel: WCProductModel? = null): WCProductMo
482478
it.groupOfQuantity = groupOfQuantity ?: -1
483479
it.combineVariationQuantities = combineVariationQuantities ?: false
484480
it.password = password
485-
// Subscription details are currently the only editable metadata fields from the app.
486-
it.metadata = subscription?.toMetadataJson().toString()
487481
}
488482
}
489483

490484
fun WCProductModel.toAppModel(): Product {
491-
val productType = ProductType.fromString(type)
492-
val subscription = if (
493-
productType == ProductType.SUBSCRIPTION || productType == ProductType.VARIABLE_SUBSCRIPTION
494-
) {
495-
SubscriptionDetailsMapper.toAppModel(this.metadata)
496-
} else {
497-
null
498-
}
499485
return Product(
500486
remoteId = this.remoteProductId,
501487
parentId = this.parentId,
@@ -580,7 +566,6 @@ fun WCProductModel.toAppModel(): Product {
580566
upsellProductIds = this.getUpsellProductIdList(),
581567
variationIds = this.getVariationIdList(),
582568
isPurchasable = this.purchasable,
583-
subscription = subscription,
584569
isSampleProduct = isSampleProduct,
585570
specialStockStatus = if (this.specialStockStatus.isNotNullOrEmpty()) {
586571
ProductStockStatus.fromString(this.specialStockStatus)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.woocommerce.android.model
2+
3+
import android.os.Parcelable
4+
import kotlinx.parcelize.Parcelize
5+
6+
/**
7+
* Container class for product and any additional details that are stored as product metadata.
8+
*
9+
* For now, the additional details include subscription details only.
10+
*
11+
* @param product The product.
12+
* @param subscription The subscription details.
13+
*/
14+
@Parcelize
15+
data class ProductAggregate(
16+
val product: Product,
17+
val subscription: SubscriptionDetails? = null
18+
) : Parcelable {
19+
val remoteId: Long
20+
get() = product.remoteId
21+
22+
val hasShipping: Boolean
23+
get() = product.hasShipping || subscription?.oneTimeShipping == true
24+
25+
fun isSame(other: ProductAggregate): Boolean {
26+
return product.isSameProduct(other.product) && subscription == other.subscription
27+
}
28+
29+
fun merge(other: ProductAggregate): ProductAggregate {
30+
return copy(
31+
product = product.mergeProduct(other.product),
32+
subscription = other.subscription
33+
)
34+
}
35+
}

WooCommerce/src/main/kotlin/com/woocommerce/android/model/SubscriptionDetailsMapper.kt

Lines changed: 60 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,81 @@
11
package com.woocommerce.android.model
22

33
import com.google.gson.Gson
4-
import com.google.gson.JsonArray
5-
import com.google.gson.JsonElement
6-
import com.google.gson.JsonObject
4+
import com.google.gson.JsonParser
75
import org.wordpress.android.fluxc.model.WCProductModel.SubscriptionMetadataKeys
86
import org.wordpress.android.fluxc.model.metadata.WCMetaData
7+
import org.wordpress.android.fluxc.model.metadata.get
98

109
object SubscriptionDetailsMapper {
1110
private val gson by lazy { Gson() }
12-
fun toAppModel(metadata: String): SubscriptionDetails? {
13-
val jsonArray = gson.fromJson(metadata, JsonArray::class.java) ?: return null
11+
fun toAppModel(metadata: List<WCMetaData>): SubscriptionDetails? {
12+
if (metadata.none { it.key in SubscriptionMetadataKeys.ALL_KEYS }) {
13+
return null
14+
}
1415

15-
val subscriptionInformation = jsonArray
16-
.mapNotNull { it as? JsonObject }
17-
.filter { jsonObject -> jsonObject[WCMetaData.KEY].asString in SubscriptionMetadataKeys.ALL_KEYS }
18-
.associate { jsonObject ->
19-
jsonObject[WCMetaData.KEY].asString to jsonObject[WCMetaData.VALUE]
20-
}
16+
val price = metadata[SubscriptionMetadataKeys.SUBSCRIPTION_PRICE]?.valueAsString?.toBigDecimalOrNull()
2117

22-
return if (subscriptionInformation.isNotEmpty()) {
23-
val price = subscriptionInformation[SubscriptionMetadataKeys.SUBSCRIPTION_PRICE]?.asString
24-
?.toBigDecimalOrNull()
25-
26-
val periodString = subscriptionInformation[SubscriptionMetadataKeys.SUBSCRIPTION_PERIOD]?.asString ?: ""
27-
val period = SubscriptionPeriod.fromValue(periodString)
28-
29-
val periodIntervalString = subscriptionInformation[SubscriptionMetadataKeys.SUBSCRIPTION_PERIOD_INTERVAL]
30-
?.asString ?: ""
31-
val periodInterval = periodIntervalString.toIntOrNull() ?: 0
32-
33-
val lengthInt = subscriptionInformation[SubscriptionMetadataKeys.SUBSCRIPTION_LENGTH]?.asString
34-
?.toIntOrNull()
35-
val length = if (lengthInt != null && lengthInt > 0) lengthInt else null
36-
37-
val signUpFee = subscriptionInformation[SubscriptionMetadataKeys.SUBSCRIPTION_SIGN_UP_FEE]?.asString
38-
?.toBigDecimalOrNull()
39-
40-
val trialPeriodString = subscriptionInformation[SubscriptionMetadataKeys.SUBSCRIPTION_TRIAL_PERIOD]
41-
?.asString
42-
val trialPeriod = trialPeriodString?.let { SubscriptionPeriod.fromValue(trialPeriodString) }
43-
44-
val trialLengthInt = subscriptionInformation[SubscriptionMetadataKeys.SUBSCRIPTION_TRIAL_LENGTH]
45-
?.asString?.toIntOrNull()
46-
val trialLength = if (trialLengthInt != null && trialLengthInt > 0) trialLengthInt else null
47-
48-
val oneTimeShipping = subscriptionInformation[SubscriptionMetadataKeys.SUBSCRIPTION_ONE_TIME_SHIPPING]
49-
?.asString == "yes"
50-
51-
val paymentsSyncDate = subscriptionInformation[SubscriptionMetadataKeys.SUBSCRIPTION_PAYMENT_SYNC_DATE]
52-
?.extractPaymentsSyncDate()
53-
54-
SubscriptionDetails(
55-
price = price,
56-
period = period,
57-
periodInterval = periodInterval,
58-
length = length,
59-
signUpFee = signUpFee,
60-
trialPeriod = trialPeriod,
61-
trialLength = trialLength,
62-
oneTimeShipping = oneTimeShipping,
63-
paymentsSyncDate = paymentsSyncDate
64-
)
65-
} else {
66-
null
67-
}
18+
val periodString = metadata[SubscriptionMetadataKeys.SUBSCRIPTION_PERIOD]?.valueAsString ?: ""
19+
val period = SubscriptionPeriod.fromValue(periodString)
20+
21+
val periodIntervalString = metadata[SubscriptionMetadataKeys.SUBSCRIPTION_PERIOD_INTERVAL]
22+
?.valueAsString ?: ""
23+
val periodInterval = periodIntervalString.toIntOrNull() ?: 0
24+
25+
val lengthInt = metadata[SubscriptionMetadataKeys.SUBSCRIPTION_LENGTH]?.valueAsString
26+
?.toIntOrNull()
27+
val length = if (lengthInt != null && lengthInt > 0) lengthInt else null
28+
29+
val signUpFee = metadata[SubscriptionMetadataKeys.SUBSCRIPTION_SIGN_UP_FEE]?.valueAsString
30+
?.toBigDecimalOrNull()
31+
32+
val trialPeriodString = metadata[SubscriptionMetadataKeys.SUBSCRIPTION_TRIAL_PERIOD]
33+
?.valueAsString
34+
val trialPeriod = trialPeriodString?.let { SubscriptionPeriod.fromValue(trialPeriodString) }
35+
36+
val trialLengthInt = metadata[SubscriptionMetadataKeys.SUBSCRIPTION_TRIAL_LENGTH]
37+
?.valueAsString?.toIntOrNull()
38+
val trialLength = if (trialLengthInt != null && trialLengthInt > 0) trialLengthInt else null
39+
40+
val oneTimeShipping = metadata[SubscriptionMetadataKeys.SUBSCRIPTION_ONE_TIME_SHIPPING]
41+
?.valueAsString == "yes"
42+
43+
val paymentsSyncDate = metadata[SubscriptionMetadataKeys.SUBSCRIPTION_PAYMENT_SYNC_DATE]
44+
?.extractPaymentsSyncDate()
45+
46+
return SubscriptionDetails(
47+
price = price,
48+
period = period,
49+
periodInterval = periodInterval,
50+
length = length,
51+
signUpFee = signUpFee,
52+
trialPeriod = trialPeriod,
53+
trialLength = trialLength,
54+
oneTimeShipping = oneTimeShipping,
55+
paymentsSyncDate = paymentsSyncDate
56+
)
57+
}
58+
59+
fun toAppModel(metadata: String): SubscriptionDetails? {
60+
val metadataList = gson.fromJson(metadata, Array<WCMetaData>::class.java)?.toList() ?: return null
61+
62+
return toAppModel(metadataList)
6863
}
6964

70-
private fun JsonElement.extractPaymentsSyncDate(): SubscriptionPaymentSyncDate? {
71-
return when {
72-
isJsonObject -> asJsonObject.let {
73-
val day = it["day"].asInt
74-
val month = it["month"].asInt
65+
private fun WCMetaData.extractPaymentsSyncDate(): SubscriptionPaymentSyncDate? {
66+
return when (isJson) {
67+
true -> value.stringValue.let {
68+
val jsonObject = JsonParser.parseString(it).asJsonObject
69+
val day = jsonObject["day"].asInt
70+
val month = jsonObject["month"].asInt
7571
if (day == 0) {
7672
SubscriptionPaymentSyncDate.None
7773
} else {
78-
SubscriptionPaymentSyncDate.MonthDay(day, month)
74+
SubscriptionPaymentSyncDate.MonthDay(month = month, day = day)
7975
}
8076
}
8177

82-
else -> asString?.toIntOrNull()?.let { day ->
78+
false -> valueAsString.toIntOrNull()?.let { day ->
8379
if (day == 0) {
8480
SubscriptionPaymentSyncDate.None
8581
} else {

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductHelper.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package com.woocommerce.android.ui.products
22

33
import com.woocommerce.android.model.Product
4+
import com.woocommerce.android.model.ProductAggregate
45
import com.woocommerce.android.model.SubscriptionDetails
56
import com.woocommerce.android.model.SubscriptionPeriod
67
import com.woocommerce.android.ui.products.ProductBackorderStatus.NotAvailable
78
import com.woocommerce.android.ui.products.ProductStatus.DRAFT
89
import com.woocommerce.android.ui.products.ProductStatus.PUBLISH
910
import com.woocommerce.android.ui.products.ProductStockStatus.InStock
10-
import com.woocommerce.android.ui.products.ProductType.SUBSCRIPTION
11-
import com.woocommerce.android.ui.products.ProductType.VARIABLE_SUBSCRIPTION
1211
import com.woocommerce.android.ui.products.settings.ProductCatalogVisibility.VISIBLE
1312
import java.math.BigDecimal
1413
import java.util.Date
@@ -93,12 +92,6 @@ object ProductHelper {
9392
variationIds = listOf(),
9493
downloads = listOf(),
9594
isPurchasable = false,
96-
subscription =
97-
if (productType == SUBSCRIPTION || productType == VARIABLE_SUBSCRIPTION) {
98-
getDefaultSubscriptionDetails()
99-
} else {
100-
null
101-
},
10295
isSampleProduct = false,
10396
parentId = 0,
10497
minAllowedQuantity = null,
@@ -111,6 +104,19 @@ object ProductHelper {
111104
)
112105
}
113106

107+
fun getDefaultProductAggregate(productType: ProductType, isVirtual: Boolean): ProductAggregate {
108+
return ProductAggregate(
109+
product = getDefaultNewProduct(productType, isVirtual),
110+
subscription = if (productType == ProductType.SUBSCRIPTION ||
111+
productType == ProductType.VARIABLE_SUBSCRIPTION
112+
) {
113+
getDefaultSubscriptionDetails()
114+
} else {
115+
null
116+
}
117+
)
118+
}
119+
114120
fun getDefaultSubscriptionDetails(): SubscriptionDetails =
115121
SubscriptionDetails(
116122
price = null,

0 commit comments

Comments
 (0)