diff --git a/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/datasource/StatsRepository.kt b/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/datasource/StatsRepository.kt index 61fd0e8632d0..cdcf1f8e24d3 100644 --- a/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/datasource/StatsRepository.kt +++ b/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/datasource/StatsRepository.kt @@ -21,6 +21,8 @@ import com.woocommerce.android.wear.util.DateUtils import com.woocommerce.commons.DataParameters.ORDERS_COUNT import com.woocommerce.commons.DataParameters.TOTAL_REVENUE import com.woocommerce.commons.DataParameters.VISITORS_TOTAL +import com.woocommerce.commons.stats.StatsTimeRange +import com.woocommerce.commons.stats.StatsUtils.toRevenueRangeId import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import org.wordpress.android.fluxc.model.SiteModel @@ -61,7 +63,8 @@ class StatsRepository @Inject constructor( site = selectedSite, granularity = StatsGranularity.DAYS, startDate = todayRange.start.formatToYYYYmmDDhhmmss(), - endDate = todayRange.end.formatToYYYYmmDDhhmmss() + endDate = todayRange.end.formatToYYYYmmDDhhmmss(), + revenueRangeId = StatsTimeRange(todayRange.start, todayRange.end).toRevenueRangeId("Wear") ) ) diff --git a/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/range/StatsTimeRangeData.kt b/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/range/StatsTimeRangeData.kt index 296a0dec55ba..abe1197636fe 100644 --- a/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/range/StatsTimeRangeData.kt +++ b/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/range/StatsTimeRangeData.kt @@ -1,15 +1,7 @@ package com.woocommerce.android.wear.ui.stats.range -import android.os.Parcelable -import kotlinx.parcelize.Parcelize +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar -import java.util.Date - -@Parcelize -data class StatsTimeRange( - val start: Date, - val end: Date -) : Parcelable abstract class StatsTimeRangeData( referenceCalendar: Calendar diff --git a/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/range/TodayRangeData.kt b/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/range/TodayRangeData.kt index 3be756505035..5ce96afb7053 100644 --- a/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/range/TodayRangeData.kt +++ b/WooCommerce-Wear/src/main/java/com/woocommerce/android/wear/ui/stats/range/TodayRangeData.kt @@ -5,6 +5,7 @@ import com.woocommerce.android.wear.extensions.formatToMMMddYYYY import com.woocommerce.android.wear.extensions.oneDayAgo import com.woocommerce.android.wear.extensions.startOfCurrentDay import com.woocommerce.android.wear.util.DateUtils +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/background/BackgroundUpdateAnalyticsRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/background/BackgroundUpdateAnalyticsRepository.kt index ca509b61f1a3..3f4965aa2474 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/background/BackgroundUpdateAnalyticsRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/background/BackgroundUpdateAnalyticsRepository.kt @@ -11,7 +11,7 @@ import com.woocommerce.android.ui.analytics.ranges.revenueStatsGranularity import com.woocommerce.android.ui.analytics.ranges.visitorStatsGranularity import com.woocommerce.android.ui.analytics.ranges.visitorSummaryStatsGranularity import com.woocommerce.android.ui.dashboard.data.StatsRepository -import com.woocommerce.android.ui.dashboard.data.asRevenueRangeId +import com.woocommerce.commons.stats.StatsUtils.asRevenueRangeId import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt index b6c09bf9024c..6bd2103b433c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt @@ -21,14 +21,14 @@ import com.woocommerce.android.ui.analytics.hub.sync.AnalyticsRepository.Product import com.woocommerce.android.ui.analytics.hub.sync.AnalyticsRepository.RevenueResult.RevenueData import com.woocommerce.android.ui.analytics.hub.sync.AnalyticsRepository.RevenueResult.RevenueError import com.woocommerce.android.ui.analytics.ranges.NotSupportedGranularity -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType import com.woocommerce.android.ui.analytics.ranges.revenueStatsGranularity import com.woocommerce.android.ui.analytics.ranges.visitorStatsGranularity import com.woocommerce.android.ui.analytics.ranges.visitorSummaryStatsGranularity import com.woocommerce.android.ui.dashboard.data.StatsRepository -import com.woocommerce.android.ui.dashboard.data.asRevenueRangeId +import com.woocommerce.commons.stats.StatsTimeRange +import com.woocommerce.commons.stats.StatsUtils.asRevenueRangeId import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeData.kt index 0237b04d036b..ffebe0b28f60 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeData.kt @@ -1,5 +1,6 @@ package com.woocommerce.android.ui.analytics.ranges +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar abstract class StatsTimeRangeData( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeSelection.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeSelection.kt index 2e867f9c9213..2fd68a362f4e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeSelection.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeSelection.kt @@ -25,18 +25,13 @@ import com.woocommerce.android.ui.analytics.ranges.data.TodayRangeData import com.woocommerce.android.ui.analytics.ranges.data.WeekToDateRangeData import com.woocommerce.android.ui.analytics.ranges.data.YearToDateRangeData import com.woocommerce.android.ui.analytics.ranges.data.YesterdayRangeData +import com.woocommerce.commons.stats.StatsTimeRange import kotlinx.parcelize.Parcelize import org.wordpress.android.fluxc.store.WCStatsStore.StatsGranularity import java.util.Calendar import java.util.Date import java.util.Locale -@Parcelize -data class StatsTimeRange( - val start: Date, - val end: Date -) : Parcelable - /** * This class represents the date range selection for the Analytics Hub and the Stats screen * diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/CustomRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/CustomRangeData.kt index 9eb07385e7a8..b4c61096dc37 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/CustomRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/CustomRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentDay import com.woocommerce.android.extensions.formatAsRangeWith import com.woocommerce.android.extensions.oneDayAgo import com.woocommerce.android.extensions.startOfCurrentDay -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastMonthRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastMonthRangeData.kt index 1992ac46f028..3c032fa56249 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastMonthRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastMonthRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentMonth import com.woocommerce.android.extensions.formatAsRangeWith import com.woocommerce.android.extensions.oneMonthAgo import com.woocommerce.android.extensions.startOfCurrentMonth -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastQuarterRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastQuarterRangeData.kt index 83d385140b00..f54801310398 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastQuarterRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastQuarterRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentQuarter import com.woocommerce.android.extensions.formatAsRangeWith import com.woocommerce.android.extensions.oneQuarterAgo import com.woocommerce.android.extensions.startOfCurrentQuarter -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastWeekRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastWeekRangeData.kt index d60c8fe24d1d..964e81ae8402 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastWeekRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastWeekRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentWeek import com.woocommerce.android.extensions.formatAsRangeWith import com.woocommerce.android.extensions.oneWeekAgo import com.woocommerce.android.extensions.startOfCurrentWeek -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastYearRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastYearRangeData.kt index fec348549229..8dce94bff2d7 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastYearRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/LastYearRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentYear import com.woocommerce.android.extensions.formatAsRangeWith import com.woocommerce.android.extensions.oneYearAgo import com.woocommerce.android.extensions.startOfCurrentYear -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/MonthToDateRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/MonthToDateRangeData.kt index ddb3ef696358..213044d7625d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/MonthToDateRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/MonthToDateRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentMonth import com.woocommerce.android.extensions.formatAsRangeWith import com.woocommerce.android.extensions.oneMonthAgo import com.woocommerce.android.extensions.startOfCurrentMonth -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/QuarterToDateRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/QuarterToDateRangeData.kt index 1942881af9d8..c829ee7b8c55 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/QuarterToDateRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/QuarterToDateRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentQuarter import com.woocommerce.android.extensions.formatAsRangeWith import com.woocommerce.android.extensions.oneQuarterAgo import com.woocommerce.android.extensions.startOfCurrentQuarter -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/TodayRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/TodayRangeData.kt index 5fd2f49cbe30..6eca9be92841 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/TodayRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/TodayRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentDay import com.woocommerce.android.extensions.formatToMMMddYYYY import com.woocommerce.android.extensions.oneDayAgo import com.woocommerce.android.extensions.startOfCurrentDay -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/WeekToDateRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/WeekToDateRangeData.kt index 53e7311fb18a..dfb54c2fa2e2 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/WeekToDateRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/WeekToDateRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentWeek import com.woocommerce.android.extensions.formatAsRangeWith import com.woocommerce.android.extensions.oneWeekAgo import com.woocommerce.android.extensions.startOfCurrentWeek -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/YearToDateRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/YearToDateRangeData.kt index 069351e97ddc..dd5fbdfeb1fd 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/YearToDateRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/YearToDateRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentYear import com.woocommerce.android.extensions.formatAsRangeWith import com.woocommerce.android.extensions.oneYearAgo import com.woocommerce.android.extensions.startOfCurrentYear -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/YesterdayRangeData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/YesterdayRangeData.kt index 7a4bab11d354..bdbd97ae9074 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/YesterdayRangeData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/ranges/data/YesterdayRangeData.kt @@ -4,8 +4,8 @@ import com.woocommerce.android.extensions.endOfCurrentDay import com.woocommerce.android.extensions.formatToMMMddYYYY import com.woocommerce.android.extensions.oneDayAgo import com.woocommerce.android.extensions.startOfCurrentDay -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeData +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/appwidgets/stats/GetWidgetStats.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/appwidgets/stats/GetWidgetStats.kt index 514d57d8e96e..b02cd5b6fe3a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/appwidgets/stats/GetWidgetStats.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/appwidgets/stats/GetWidgetStats.kt @@ -54,7 +54,8 @@ class GetWidgetStats @Inject constructor( visitorStatsGranularity = rangeSelection.visitorStatsGranularity, forced = true, includeVisitorStats = areVisitorStatsSupported, - site = siteModel + site = siteModel, + medium = "Widget", ) fetchedStats.fold( onSuccess = { stats -> diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/coupons/CouponRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/coupons/CouponRepository.kt index 47a3193ee0c8..88b6449556f6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/coupons/CouponRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/coupons/CouponRepository.kt @@ -7,11 +7,11 @@ import com.woocommerce.android.model.Coupon import com.woocommerce.android.model.CouponPerformanceReport import com.woocommerce.android.model.toAppModel import com.woocommerce.android.tools.SelectedSite -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.coupons.tracking.StoreManagementCouponCreationFlowTrackerEventProvider import com.woocommerce.android.ui.woopos.home.items.coupons.creation.WooPosCouponCreationFlowTrackerEventProvider import com.woocommerce.android.util.DateUtils import com.woocommerce.android.util.WooLog +import com.woocommerce.commons.stats.StatsTimeRange import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsCard.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsCard.kt index b3b9cd80f4d4..5717cad5871f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsCard.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsCard.kt @@ -33,7 +33,6 @@ import com.woocommerce.android.R import com.woocommerce.android.extensions.navigateSafely import com.woocommerce.android.model.DashboardWidget import com.woocommerce.android.model.DashboardWidget.Type.COUPONS -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType import com.woocommerce.android.ui.compose.animations.SkeletonView import com.woocommerce.android.ui.compose.rememberNavController @@ -49,6 +48,7 @@ import com.woocommerce.android.ui.dashboard.coupons.DashboardCouponsViewModel.Da import com.woocommerce.android.ui.dashboard.coupons.DashboardCouponsViewModel.State import com.woocommerce.android.ui.dashboard.defaultHideMenuEntry import com.woocommerce.android.viewmodel.MultiLiveEvent +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Date @Composable diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsViewModel.kt index 314eeccbe77f..ffe4e06d8bc5 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsViewModel.kt @@ -10,7 +10,6 @@ import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.model.CouponPerformanceReport import com.woocommerce.android.model.DashboardWidget -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType import com.woocommerce.android.ui.coupons.CouponRepository @@ -24,6 +23,7 @@ import com.woocommerce.android.util.CouponUtils import com.woocommerce.android.util.WooLog import com.woocommerce.android.viewmodel.MultiLiveEvent import com.woocommerce.android.viewmodel.ScopedViewModel +import com.woocommerce.commons.stats.StatsTimeRange import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/data/CustomDateRangeDataStore.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/data/CustomDateRangeDataStore.kt index 8e0f0a50c06e..bfdeb3974643 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/data/CustomDateRangeDataStore.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/data/CustomDateRangeDataStore.kt @@ -1,9 +1,9 @@ package com.woocommerce.android.ui.dashboard.data import androidx.datastore.core.DataStore -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.mystore.data.CustomDateRange import com.woocommerce.android.util.WooLog +import com.woocommerce.commons.stats.StatsTimeRange import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.map diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/data/StatsRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/data/StatsRepository.kt index c4efb70a0828..26108f5e0489 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/data/StatsRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/data/StatsRepository.kt @@ -7,11 +7,12 @@ import com.woocommerce.android.extensions.semverCompareTo import com.woocommerce.android.network.giftcard.GiftCardRestClient import com.woocommerce.android.network.giftcard.toWCModel import com.woocommerce.android.tools.SelectedSite -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.util.CoroutineDispatchers import com.woocommerce.android.util.GetWooCorePluginCachedVersion import com.woocommerce.android.util.WooLog import com.woocommerce.android.util.WooLog.T.DASHBOARD +import com.woocommerce.commons.stats.StatsTimeRange +import com.woocommerce.commons.stats.StatsUtils.toRevenueRangeId import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -63,7 +64,7 @@ class StatsRepository @Inject constructor( range: StatsTimeRange, granularity: StatsGranularity, forced: Boolean, - revenueRangeId: String = "" + revenueRangeId: String, ): Result = fetchRevenueStats( range = range, granularity = granularity, @@ -76,7 +77,7 @@ class StatsRepository @Inject constructor( range: StatsTimeRange, granularity: StatsGranularity, forced: Boolean, - revenueRangeId: String = "", + revenueRangeId: String, site: SiteModel ): Result { val result = wcStatsStore.fetchRevenueStats( @@ -308,13 +309,15 @@ class StatsRepository @Inject constructor( * Even if the includeVisitorStats flag is set to true, errors fetching visitor * will be handled as null and only errors fetching the revenue stats will be processed. */ + @Suppress("LongParameterList") suspend fun fetchStats( range: StatsTimeRange, revenueStatsGranularity: StatsGranularity, visitorStatsGranularity: StatsGranularity, forced: Boolean, includeVisitorStats: Boolean, - site: SiteModel = selectedSite.get() + site: SiteModel = selectedSite.get(), + medium: String, ): Result = coroutineScope { val fetchVisitorStats = if (includeVisitorStats) { async { @@ -334,6 +337,7 @@ class StatsRepository @Inject constructor( range = range, granularity = revenueStatsGranularity, forced = forced, + revenueRangeId = range.toRevenueRangeId(medium), site = site ) } @@ -408,12 +412,3 @@ class StatsRepository @Inject constructor( } } } - -fun String.asRevenueRangeId( - startDate: Date, - endDate: Date -): String { - val startDateString = startDate.formatToYYYYmmDD() - val endDateString = endDate.formatToYYYYmmDD() - return "$this$startDateString$endDateString" -} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/domain/DashboardDateRangeFormatter.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/domain/DashboardDateRangeFormatter.kt index da4c4a204913..dc0b42ce12b8 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/domain/DashboardDateRangeFormatter.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/domain/DashboardDateRangeFormatter.kt @@ -7,8 +7,8 @@ import com.woocommerce.android.extensions.formatToMMMMyyyy import com.woocommerce.android.extensions.formatToYYYY import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType -import com.woocommerce.android.ui.dashboard.data.asRevenueRangeId import com.woocommerce.android.util.DateUtils +import com.woocommerce.commons.stats.StatsUtils.asRevenueRangeId import javax.inject.Inject class DashboardDateRangeFormatter @Inject constructor(private val dateUtils: DateUtils) { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsCard.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsCard.kt index 93dde90fc90c..36cd5b8ec942 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsCard.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsCard.kt @@ -24,7 +24,6 @@ import androidx.lifecycle.lifecycleScope import com.woocommerce.android.R import com.woocommerce.android.extensions.navigateSafely import com.woocommerce.android.model.DashboardWidget -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType import com.woocommerce.android.ui.compose.rememberNavController import com.woocommerce.android.ui.dashboard.DashboardDateRangeHeader @@ -38,6 +37,7 @@ import com.woocommerce.android.ui.dashboard.defaultHideMenuEntry import com.woocommerce.android.util.CurrencyFormatter import com.woocommerce.android.util.DateUtils import com.woocommerce.android.viewmodel.MultiLiveEvent +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Date @Composable diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsViewModel.kt index db87c15eb108..af41cebd313c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsViewModel.kt @@ -16,7 +16,6 @@ import com.woocommerce.android.model.DashboardWidget import com.woocommerce.android.tools.NetworkStatus import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.analytics.hub.sync.AnalyticsUpdateDataStore -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType import com.woocommerce.android.ui.dashboard.DashboardStatsUsageTracksEventEmitter @@ -32,6 +31,7 @@ import com.woocommerce.android.util.DateUtils import com.woocommerce.android.util.TimezoneProvider import com.woocommerce.android.viewmodel.MultiLiveEvent import com.woocommerce.android.viewmodel.ScopedViewModel +import com.woocommerce.commons.stats.StatsTimeRange import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/GetStats.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/GetStats.kt index 807f59aecb80..822e9f26bb65 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/GetStats.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/stats/GetStats.kt @@ -11,7 +11,6 @@ import com.woocommerce.android.ui.analytics.ranges.visitorStatsGranularity import com.woocommerce.android.ui.analytics.ranges.visitorSummaryStatsGranularity import com.woocommerce.android.ui.dashboard.data.StatsRepository import com.woocommerce.android.ui.dashboard.data.StatsRepository.StatsException -import com.woocommerce.android.ui.dashboard.data.asRevenueRangeId import com.woocommerce.android.ui.dashboard.stats.GetStats.LoadStatsResult.PluginNotActive import com.woocommerce.android.ui.dashboard.stats.GetStats.LoadStatsResult.RevenueStatsError import com.woocommerce.android.ui.dashboard.stats.GetStats.LoadStatsResult.RevenueStatsSuccess @@ -20,6 +19,7 @@ import com.woocommerce.android.ui.dashboard.stats.GetStats.LoadStatsResult.Visit import com.woocommerce.android.ui.dashboard.stats.GetStats.LoadStatsResult.VisitorsStatsSuccess import com.woocommerce.android.util.CoroutineDispatchers import com.woocommerce.android.util.ResultWithOutdatedFlag +import com.woocommerce.commons.stats.StatsUtils.asRevenueRangeId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/topperformers/DashboardTopPerformersViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/topperformers/DashboardTopPerformersViewModel.kt index 4f61f968d926..7b5f9d73dc57 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/topperformers/DashboardTopPerformersViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/topperformers/DashboardTopPerformersViewModel.kt @@ -17,7 +17,6 @@ import com.woocommerce.android.model.DashboardWidget import com.woocommerce.android.tools.NetworkStatus import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.analytics.hub.sync.AnalyticsUpdateDataStore.AnalyticData -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType import com.woocommerce.android.ui.dashboard.DashboardStatsUsageTracksEventEmitter @@ -37,6 +36,7 @@ import com.woocommerce.android.util.DateUtils import com.woocommerce.android.viewmodel.MultiLiveEvent import com.woocommerce.android.viewmodel.ResourceProvider import com.woocommerce.android.viewmodel.ScopedViewModel +import com.woocommerce.commons.stats.StatsTimeRange import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/topperformers/DashboardTopPerformersWidgetCard.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/topperformers/DashboardTopPerformersWidgetCard.kt index 14abd17fe4aa..fa60f4726da9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/topperformers/DashboardTopPerformersWidgetCard.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/topperformers/DashboardTopPerformersWidgetCard.kt @@ -36,7 +36,6 @@ import com.woocommerce.android.NavGraphMainDirections import com.woocommerce.android.R import com.woocommerce.android.extensions.navigateSafely import com.woocommerce.android.model.DashboardWidget -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType import com.woocommerce.android.ui.compose.animations.SkeletonView import com.woocommerce.android.ui.compose.component.ProductThumbnail @@ -60,6 +59,7 @@ import com.woocommerce.android.ui.dashboard.topperformers.DashboardTopPerformers import com.woocommerce.android.ui.dashboard.topperformers.DashboardTopPerformersViewModel.TopPerformersState import com.woocommerce.android.ui.products.details.ProductDetailFragment.Mode.ShowProduct import com.woocommerce.android.viewmodel.MultiLiveEvent.Event +import com.woocommerce.commons.stats.StatsTimeRange import java.util.Calendar import java.util.Date import java.util.Locale diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/wear/GetWearableMyStoreStats.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/wear/GetWearableMyStoreStats.kt index 2e22ce66ba39..499aa10206e4 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/wear/GetWearableMyStoreStats.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/wear/GetWearableMyStoreStats.kt @@ -31,7 +31,8 @@ class GetWearableMyStoreStats @Inject constructor( visitorStatsGranularity = todayRange.visitorStatsGranularity, forced = true, includeVisitorStats = true, - site = selectedSite + site = selectedSite, + medium = "Wear", ).getOrNull() } } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeSelectionTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeSelectionTest.kt index f5409419ce65..21b8031be292 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeSelectionTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/analytics/ranges/StatsTimeRangeSelectionTest.kt @@ -17,6 +17,7 @@ import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.Selec import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType.WEEK_TO_DATE import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType.YEAR_TO_DATE import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType.YESTERDAY +import com.woocommerce.commons.stats.StatsTimeRange import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Test diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/appwidgets/stats/GetWidgetStatsTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/appwidgets/stats/GetWidgetStatsTest.kt index 19c0455a6831..317530622101 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/appwidgets/stats/GetWidgetStatsTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/appwidgets/stats/GetWidgetStatsTest.kt @@ -16,6 +16,7 @@ import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.WCRevenueStatsModel import java.util.Calendar @@ -49,7 +50,7 @@ class GetWidgetStatsTest : BaseUnitTest() { "2020-11-01" to 3, "2020-12-01" to 4 ), - revenue = WCRevenueStatsModel(), + revenue = WCRevenueStatsModel(LocalId(1), "", "", "", "", "", ""), currencyCode = "USD" ) @@ -128,8 +129,17 @@ class GetWidgetStatsTest : BaseUnitTest() { whenever(networkStatus.isConnected()).thenReturn(true) // Given fetching the stats fails - whenever(statsRepository.fetchStats(any(), any(), any(), eq(true), eq(true), eq(defaultSiteModel))) - .thenReturn(Result.failure(Exception(defaultErrorMessage))) + whenever( + statsRepository.fetchStats( + any(), + any(), + any(), + eq(true), + eq(true), + eq(defaultSiteModel), + eq("Widget"), + ) + ).thenReturn(Result.failure(Exception(defaultErrorMessage))) // When GetWidgetStats is invoked val result = sut.invoke(defaultRange, defaultSiteModel) @@ -148,8 +158,17 @@ class GetWidgetStatsTest : BaseUnitTest() { whenever(networkStatus.isConnected()).thenReturn(true) // Given fetching the stats succeed - whenever(statsRepository.fetchStats(any(), any(), any(), eq(true), eq(true), eq(defaultSiteModel))) - .thenReturn(Result.success(defaultResponse)) + whenever( + statsRepository.fetchStats( + any(), + any(), + any(), + eq(true), + eq(true), + eq(defaultSiteModel), + eq("Widget"), + ) + ).thenReturn(Result.success(defaultResponse)) // When GetWidgetStats is invoked val result = sut.invoke(defaultRange, defaultSiteModel) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsViewModelTest.kt index 605b7e92498d..3fbb8f938698 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/coupons/DashboardCouponsViewModelTest.kt @@ -5,7 +5,6 @@ import com.woocommerce.android.AppPrefsWrapper import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.model.Coupon import com.woocommerce.android.model.CouponPerformanceReport -import com.woocommerce.android.ui.analytics.ranges.StatsTimeRange import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.SelectionType import com.woocommerce.android.ui.coupons.CouponRepository import com.woocommerce.android.ui.dashboard.DashboardViewModel @@ -20,6 +19,7 @@ import com.woocommerce.android.util.captureValues import com.woocommerce.android.util.getOrAwaitValue import com.woocommerce.android.util.runAndCaptureValues import com.woocommerce.android.viewmodel.BaseUnitTest +import com.woocommerce.commons.stats.StatsTimeRange import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/data/StatsRepositoryTests.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/data/StatsRepositoryTests.kt index 1c1dd1994761..d975e6172a14 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/data/StatsRepositoryTests.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/data/StatsRepositoryTests.kt @@ -14,6 +14,7 @@ import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import org.wordpress.android.fluxc.action.WCStatsAction +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.WCBundleStats import org.wordpress.android.fluxc.model.WCGiftCardStats @@ -78,7 +79,6 @@ class StatsRepositoryTests : BaseUnitTest() { ) val revenueStatsResponse = WCStatsStore.OnWCRevenueStatsChanged( - rowsAffected = 2, granularity = granularity, startDate = startDate, endDate = endDate @@ -91,14 +91,15 @@ class StatsRepositoryTests : BaseUnitTest() { .thenReturn(emptyMap()) whenever(wcStatsStore.fetchRevenueStats(any())).thenReturn(revenueStatsResponse) whenever(wcStatsStore.getRawRevenueStats(eq(defaultSiteModel), eq(granularity), eq(startDate), eq(endDate))) - .thenReturn(WCRevenueStatsModel()) + .thenReturn(WCRevenueStatsModel(LocalId(1), "", "", "", "", "", "")) val result = sut.fetchStats( range = defaultRange, revenueStatsGranularity = WCStatsStore.StatsGranularity.DAYS, visitorStatsGranularity = WCStatsStore.StatsGranularity.DAYS, forced = true, - includeVisitorStats = true + includeVisitorStats = true, + medium = "Widget", ) val model = result.getOrNull() @@ -119,7 +120,6 @@ class StatsRepositoryTests : BaseUnitTest() { } val revenueStatsResponse = WCStatsStore.OnWCRevenueStatsChanged( - rowsAffected = 2, granularity = granularity, startDate = startDate, endDate = endDate @@ -130,14 +130,15 @@ class StatsRepositoryTests : BaseUnitTest() { whenever(wcStatsStore.fetchNewVisitorStats(any())).thenReturn(visitorStatsResponse) whenever(wcStatsStore.fetchRevenueStats(any())).thenReturn(revenueStatsResponse) whenever(wcStatsStore.getRawRevenueStats(eq(defaultSiteModel), eq(granularity), eq(startDate), eq(endDate))) - .thenReturn(WCRevenueStatsModel()) + .thenReturn(WCRevenueStatsModel(LocalId(1), "", "", "", "", "", "")) val result = sut.fetchStats( range = defaultRange, revenueStatsGranularity = WCStatsStore.StatsGranularity.DAYS, visitorStatsGranularity = WCStatsStore.StatsGranularity.DAYS, forced = true, - includeVisitorStats = true + includeVisitorStats = true, + medium = "Widget", ) val model = result.getOrNull() @@ -157,7 +158,7 @@ class StatsRepositoryTests : BaseUnitTest() { date = startDate ) - val revenueStatsResponse = WCStatsStore.OnWCRevenueStatsChanged(0, granularity) + val revenueStatsResponse = WCStatsStore.OnWCRevenueStatsChanged(granularity) .also { it.error = WCStatsStore.OrderStatsError() } whenever(selectedSite.get()).thenReturn(defaultSiteModel) @@ -170,7 +171,8 @@ class StatsRepositoryTests : BaseUnitTest() { revenueStatsGranularity = WCStatsStore.StatsGranularity.DAYS, visitorStatsGranularity = WCStatsStore.StatsGranularity.DAYS, forced = true, - includeVisitorStats = true + includeVisitorStats = true, + medium = "Widget", ) assertThat(result.isFailure).isEqualTo(true) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsViewModelTest.kt index 27d868d80465..d18258a0947b 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/stats/DashboardStatsViewModelTest.kt @@ -39,6 +39,7 @@ import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.WCRevenueStatsModel import org.wordpress.android.fluxc.store.WooCommerceStore @@ -48,7 +49,7 @@ class DashboardStatsViewModelTest : BaseUnitTest() { val DEFAULT_SELECTION_TYPE = StatsTimeRangeSelection.SelectionType.TODAY val ANY_SELECTION_TYPE = StatsTimeRangeSelection.SelectionType.WEEK_TO_DATE const val DEFAULT_LAST_UPDATE = 1690382344865L - val ANY_REVENUE_STATS = WCRevenueStatsModel() + val ANY_REVENUE_STATS = WCRevenueStatsModel(LocalId(1), "", "", "", "", "", "") } private val getStats: GetStats = mock { diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/stats/GetStatsTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/stats/GetStatsTest.kt index 97045cffb433..c0e62fd18e02 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/stats/GetStatsTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/dashboard/stats/GetStatsTest.kt @@ -24,6 +24,7 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.WCRevenueStatsModel import org.wordpress.android.fluxc.store.WCStatsStore.OrderStatsError @@ -402,7 +403,7 @@ class GetStatsTest : BaseUnitTest() { calendar = Calendar.getInstance(), locale = Locale.getDefault() ) - val ANY_REVENUE_STATS = WCRevenueStatsModel() + val ANY_REVENUE_STATS = WCRevenueStatsModel(LocalId(1), "", "", "", "", "", "") val ANY_VISITOR_STATS = mapOf( "2020-10-01" to 1, "2020-11-01" to 3, diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml index 47a13c5023e2..b301ab416e30 100644 --- a/config/detekt/baseline.xml +++ b/config/detekt/baseline.xml @@ -544,7 +544,6 @@ UnitTestNamingRule:CreationFocusedOrderCreateEditViewModelTest.kt$CreationFocusedOrderCreateEditViewModelTest$@Test fun `when regular product and variation selected, should update order's items`() UnitTestNamingRule:CreationFocusedOrderCreateEditViewModelTest.kt$CreationFocusedOrderCreateEditViewModelTest$@Test fun `when removing a fee, do not remove the rest of fees`() UnitTestNamingRule:CreationFocusedOrderCreateEditViewModelTest.kt$CreationFocusedOrderCreateEditViewModelTest$@Test fun `when removing product, should make view not editable`() - UnitTestNamingRule:CurrencyPositionConverterTest.kt$CurrencyPositionConverterTest$@Test fun `returns default value if the db-stored value is invalid`() UnitTestNamingRule:CurrentTimeProviderTest.kt$CurrentTimeProviderTest$@Test fun `always returns current date`() UnitTestNamingRule:CustomPackageCreationDataTest.kt$CustomPackageCreationDataTest$@Test fun `isValid returns false when any dimension field is empty`() UnitTestNamingRule:CustomPackageCreationDataTest.kt$CustomPackageCreationDataTest$@Test fun `isValid returns false when saveAsTemplate is true and name or weight is empty`() @@ -2239,9 +2238,6 @@ UnitTestNamingRule:WCProductStoreTest.kt$WCProductStoreTest$@Test fun testUpdateProduct() UnitTestNamingRule:WCProductStoreTest.kt$WCProductStoreTest$@Test fun testUpdateVariation() UnitTestNamingRule:WCProductStoreTest.kt$WCProductStoreTest$@Test fun testVerifySkuExistsLocally() - UnitTestNamingRule:WCRevenueStatsModelTest.kt$WCRevenueStatsModelTest$@Test fun `should assign null value if number of sold items exceeds integer range`() - UnitTestNamingRule:WCRevenueStatsModelTest.kt$WCRevenueStatsModelTest$@Test fun `should assign null value if number of sold items is negative`() - UnitTestNamingRule:WCRevenueStatsModelTest.kt$WCRevenueStatsModelTest$@Test fun `should correctly parse value if number of sold items is within integer limits`() UnitTestNamingRule:WCSSRModelCachingFetcherTest.kt$WCSSRModelCachingFetcherTest$@Test fun `given cached value when load called again then store is not called`() UnitTestNamingRule:WCSSRModelCachingFetcherTest.kt$WCSSRModelCachingFetcherTest$@Test fun `given empty cache when load called then fetches from remote`() UnitTestNamingRule:WCSSRModelCachingFetcherTest.kt$WCSSRModelCachingFetcherTest$@Test fun `given remote failure when load called then returns error`() @@ -2269,8 +2265,6 @@ UnitTestNamingRule:WCShippingLabelStoreTest.kt$WCShippingLabelStoreTest$@Test fun `purchase shipping labels with polling one label failed`() UnitTestNamingRule:WCShippingLabelStoreTest.kt$WCShippingLabelStoreTest$@Test fun `refund shipping label for order`() UnitTestNamingRule:WCShippingLabelStoreTest.kt$WCShippingLabelStoreTest$@Test fun `verify shipping address`() - UnitTestNamingRule:WCStatsSqlUtilsTest.kt$WCStatsSqlUtilsTest$@Test @Suppress("LongMethod") fun testGetRawRevenueStatsForSiteAndUnit() - UnitTestNamingRule:WCStatsSqlUtilsTest.kt$WCStatsSqlUtilsTest$@Test @Suppress("LongMethod") fun testSimpleInsertionAndRetrievalOfRevenueStats() UnitTestNamingRule:WCTaxStoreTest.kt$WCTaxStoreTest$@Test fun `get stored tax class list for site`() UnitTestNamingRule:WCUserStoreTest.kt$WCUserStoreTest$@Test fun `fetch user role`() UnitTestNamingRule:WCUserStoreTest.kt$WCUserStoreTest$@Test fun `get user role from db`() diff --git a/libs/commons/build.gradle b/libs/commons/build.gradle index 74067c06b0c2..fe85288b42ac 100644 --- a/libs/commons/build.gradle +++ b/libs/commons/build.gradle @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.parcelize) alias(libs.plugins.dependency.analysis) } diff --git a/libs/commons/src/main/java/com/woocommerce/commons/stats/DateUtils.kt b/libs/commons/src/main/java/com/woocommerce/commons/stats/DateUtils.kt new file mode 100644 index 000000000000..fd7ecca14152 --- /dev/null +++ b/libs/commons/src/main/java/com/woocommerce/commons/stats/DateUtils.kt @@ -0,0 +1,23 @@ +package com.woocommerce.commons.stats + +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +object DateUtils { + private const val DATE_FORMAT_DEFAULT = "yyyy-MM-dd" + + fun getYearMonthDayStringFromDate(date: Date): String = formatDate(DATE_FORMAT_DEFAULT, date) + + /** + * returns a [String] formatted + * based on {@param pattern} and {@param date} + */ + fun formatDate( + pattern: String, + date: Date + ): String { + val dateFormat = SimpleDateFormat(pattern, Locale.ROOT) + return dateFormat.format(date) + } +} diff --git a/libs/commons/src/main/java/com/woocommerce/commons/stats/StatsTimeRange.kt b/libs/commons/src/main/java/com/woocommerce/commons/stats/StatsTimeRange.kt new file mode 100644 index 000000000000..c706741f26a4 --- /dev/null +++ b/libs/commons/src/main/java/com/woocommerce/commons/stats/StatsTimeRange.kt @@ -0,0 +1,11 @@ +package com.woocommerce.commons.stats + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import java.util.Date + +@Parcelize +data class StatsTimeRange( + val start: Date, + val end: Date +) : Parcelable diff --git a/libs/commons/src/main/java/com/woocommerce/commons/stats/StatsUtils.kt b/libs/commons/src/main/java/com/woocommerce/commons/stats/StatsUtils.kt new file mode 100644 index 000000000000..4a1c75a1ee19 --- /dev/null +++ b/libs/commons/src/main/java/com/woocommerce/commons/stats/StatsUtils.kt @@ -0,0 +1,21 @@ +package com.woocommerce.commons.stats + +import java.util.Date + +object StatsUtils { + fun String.asRevenueRangeId( + startDate: Date, + endDate: Date + ): String { + return StatsTimeRange( + startDate, + endDate + ).toRevenueRangeId(this) + } + + fun StatsTimeRange.toRevenueRangeId(medium: String): String { + return medium + + DateUtils.getYearMonthDayStringFromDate(start) + + DateUtils.getYearMonthDayStringFromDate(end) + } +} diff --git a/libs/fluxc-plugin/schemas/org.wordpress.android.fluxc.persistence.WCAndroidDatabase/67.json b/libs/fluxc-plugin/schemas/org.wordpress.android.fluxc.persistence.WCAndroidDatabase/67.json new file mode 100644 index 000000000000..38090fa5dea2 --- /dev/null +++ b/libs/fluxc-plugin/schemas/org.wordpress.android.fluxc.persistence.WCAndroidDatabase/67.json @@ -0,0 +1,4139 @@ +{ + "formatVersion": 1, + "database": { + "version": 67, + "identityHash": "116ada8d6e7616a6f6fffe7229924e8b", + "entities": [ + { + "tableName": "AddonEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`addonLocalId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `globalGroupLocalId` INTEGER, `productRemoteId` INTEGER, `localSiteId` INTEGER, `type` TEXT NOT NULL, `display` TEXT, `name` TEXT NOT NULL, `titleFormat` TEXT NOT NULL, `description` TEXT, `required` INTEGER NOT NULL, `position` INTEGER NOT NULL, `restrictions` TEXT, `priceType` TEXT, `price` TEXT, `min` INTEGER, `max` INTEGER, FOREIGN KEY(`globalGroupLocalId`) REFERENCES `GlobalAddonGroupEntity`(`globalGroupLocalId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "addonLocalId", + "columnName": "addonLocalId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "globalGroupLocalId", + "columnName": "globalGroupLocalId", + "affinity": "INTEGER" + }, + { + "fieldPath": "productRemoteId", + "columnName": "productRemoteId", + "affinity": "INTEGER" + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "display", + "columnName": "display", + "affinity": "TEXT" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "titleFormat", + "columnName": "titleFormat", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "required", + "columnName": "required", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "restrictions", + "columnName": "restrictions", + "affinity": "TEXT" + }, + { + "fieldPath": "priceType", + "columnName": "priceType", + "affinity": "TEXT" + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "TEXT" + }, + { + "fieldPath": "min", + "columnName": "min", + "affinity": "INTEGER" + }, + { + "fieldPath": "max", + "columnName": "max", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "addonLocalId" + ] + }, + "indices": [ + { + "name": "index_AddonEntity_globalGroupLocalId", + "unique": false, + "columnNames": [ + "globalGroupLocalId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AddonEntity_globalGroupLocalId` ON `${TABLE_NAME}` (`globalGroupLocalId`)" + } + ], + "foreignKeys": [ + { + "table": "GlobalAddonGroupEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "globalGroupLocalId" + ], + "referencedColumns": [ + "globalGroupLocalId" + ] + } + ] + }, + { + "tableName": "AddonOptionEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`addonOptionLocalId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addonLocalId` INTEGER NOT NULL, `priceType` TEXT NOT NULL, `label` TEXT, `price` TEXT, `image` TEXT, FOREIGN KEY(`addonLocalId`) REFERENCES `AddonEntity`(`addonLocalId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "addonOptionLocalId", + "columnName": "addonOptionLocalId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "addonLocalId", + "columnName": "addonLocalId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "priceType", + "columnName": "priceType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "TEXT" + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "addonOptionLocalId" + ] + }, + "indices": [ + { + "name": "index_AddonOptionEntity_addonLocalId", + "unique": false, + "columnNames": [ + "addonLocalId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AddonOptionEntity_addonLocalId` ON `${TABLE_NAME}` (`addonLocalId`)" + } + ], + "foreignKeys": [ + { + "table": "AddonEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "addonLocalId" + ], + "referencedColumns": [ + "addonLocalId" + ] + } + ] + }, + { + "tableName": "Coupons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `localSiteId` INTEGER NOT NULL, `code` TEXT, `amount` TEXT, `dateCreated` TEXT, `dateCreatedGmt` TEXT, `dateModified` TEXT, `dateModifiedGmt` TEXT, `discountType` TEXT, `description` TEXT, `dateExpires` TEXT, `dateExpiresGmt` TEXT, `usageCount` INTEGER, `isForIndividualUse` INTEGER, `usageLimit` INTEGER, `usageLimitPerUser` INTEGER, `limitUsageToXItems` INTEGER, `isShippingFree` INTEGER, `areSaleItemsExcluded` INTEGER, `minimumAmount` TEXT, `maximumAmount` TEXT, `includedProductIds` TEXT, `excludedProductIds` TEXT, `includedCategoryIds` TEXT, `excludedCategoryIds` TEXT, PRIMARY KEY(`id`, `localSiteId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT" + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "TEXT" + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "TEXT" + }, + { + "fieldPath": "dateCreatedGmt", + "columnName": "dateCreatedGmt", + "affinity": "TEXT" + }, + { + "fieldPath": "dateModified", + "columnName": "dateModified", + "affinity": "TEXT" + }, + { + "fieldPath": "dateModifiedGmt", + "columnName": "dateModifiedGmt", + "affinity": "TEXT" + }, + { + "fieldPath": "discountType", + "columnName": "discountType", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "dateExpires", + "columnName": "dateExpires", + "affinity": "TEXT" + }, + { + "fieldPath": "dateExpiresGmt", + "columnName": "dateExpiresGmt", + "affinity": "TEXT" + }, + { + "fieldPath": "usageCount", + "columnName": "usageCount", + "affinity": "INTEGER" + }, + { + "fieldPath": "isForIndividualUse", + "columnName": "isForIndividualUse", + "affinity": "INTEGER" + }, + { + "fieldPath": "usageLimit", + "columnName": "usageLimit", + "affinity": "INTEGER" + }, + { + "fieldPath": "usageLimitPerUser", + "columnName": "usageLimitPerUser", + "affinity": "INTEGER" + }, + { + "fieldPath": "limitUsageToXItems", + "columnName": "limitUsageToXItems", + "affinity": "INTEGER" + }, + { + "fieldPath": "isShippingFree", + "columnName": "isShippingFree", + "affinity": "INTEGER" + }, + { + "fieldPath": "areSaleItemsExcluded", + "columnName": "areSaleItemsExcluded", + "affinity": "INTEGER" + }, + { + "fieldPath": "minimumAmount", + "columnName": "minimumAmount", + "affinity": "TEXT" + }, + { + "fieldPath": "maximumAmount", + "columnName": "maximumAmount", + "affinity": "TEXT" + }, + { + "fieldPath": "includedProductIds", + "columnName": "includedProductIds", + "affinity": "TEXT" + }, + { + "fieldPath": "excludedProductIds", + "columnName": "excludedProductIds", + "affinity": "TEXT" + }, + { + "fieldPath": "includedCategoryIds", + "columnName": "includedCategoryIds", + "affinity": "TEXT" + }, + { + "fieldPath": "excludedCategoryIds", + "columnName": "excludedCategoryIds", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id", + "localSiteId" + ] + } + }, + { + "tableName": "CouponEmails", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`couponId` INTEGER NOT NULL, `localSiteId` INTEGER NOT NULL, `email` TEXT NOT NULL, PRIMARY KEY(`couponId`, `localSiteId`, `email`), FOREIGN KEY(`couponId`, `localSiteId`) REFERENCES `Coupons`(`id`, `localSiteId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "couponId", + "columnName": "couponId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "couponId", + "localSiteId", + "email" + ] + }, + "foreignKeys": [ + { + "table": "Coupons", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "couponId", + "localSiteId" + ], + "referencedColumns": [ + "id", + "localSiteId" + ] + } + ] + }, + { + "tableName": "GlobalAddonGroupEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalGroupLocalId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `restrictedCategoriesIds` TEXT NOT NULL, `localSiteId` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "globalGroupLocalId", + "columnName": "globalGroupLocalId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "restrictedCategoriesIds", + "columnName": "restrictedCategoriesIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "globalGroupLocalId" + ] + } + }, + { + "tableName": "OrderNotes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `noteId` INTEGER NOT NULL, `orderId` INTEGER NOT NULL, `dateCreated` TEXT, `note` TEXT, `author` TEXT, `isSystemNote` INTEGER NOT NULL, `isCustomerNote` INTEGER NOT NULL, PRIMARY KEY(`localSiteId`, `noteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "TEXT" + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT" + }, + { + "fieldPath": "author", + "columnName": "author", + "affinity": "TEXT" + }, + { + "fieldPath": "isSystemNote", + "columnName": "isSystemNote", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCustomerNote", + "columnName": "isCustomerNote", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "noteId" + ] + } + }, + { + "tableName": "OrderEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `orderId` INTEGER NOT NULL, `number` TEXT NOT NULL, `status` TEXT NOT NULL, `currency` TEXT NOT NULL, `orderKey` TEXT NOT NULL, `dateCreated` TEXT NOT NULL, `dateModified` TEXT NOT NULL, `total` TEXT NOT NULL, `totalTax` TEXT NOT NULL, `shippingTotal` TEXT NOT NULL, `paymentMethod` TEXT NOT NULL, `paymentMethodTitle` TEXT NOT NULL, `datePaid` TEXT NOT NULL, `pricesIncludeTax` INTEGER NOT NULL, `customerNote` TEXT NOT NULL, `discountTotal` TEXT NOT NULL, `discountCodes` TEXT NOT NULL, `refundTotal` TEXT NOT NULL, `customerId` INTEGER NOT NULL DEFAULT 0, `billingFirstName` TEXT NOT NULL, `billingLastName` TEXT NOT NULL, `billingCompany` TEXT NOT NULL, `billingAddress1` TEXT NOT NULL, `billingAddress2` TEXT NOT NULL, `billingCity` TEXT NOT NULL, `billingState` TEXT NOT NULL, `billingPostcode` TEXT NOT NULL, `billingCountry` TEXT NOT NULL, `billingEmail` TEXT NOT NULL, `billingPhone` TEXT NOT NULL, `shippingFirstName` TEXT NOT NULL, `shippingLastName` TEXT NOT NULL, `shippingCompany` TEXT NOT NULL, `shippingAddress1` TEXT NOT NULL, `shippingAddress2` TEXT NOT NULL, `shippingCity` TEXT NOT NULL, `shippingState` TEXT NOT NULL, `shippingPostcode` TEXT NOT NULL, `shippingCountry` TEXT NOT NULL, `shippingPhone` TEXT NOT NULL, `lineItems` TEXT NOT NULL, `shippingLines` TEXT NOT NULL, `feeLines` TEXT NOT NULL, `taxLines` TEXT NOT NULL, `couponLines` TEXT NOT NULL DEFAULT '', `metaData` TEXT NOT NULL, `paymentUrl` TEXT NOT NULL DEFAULT '', `isEditable` INTEGER NOT NULL DEFAULT 1, `needsPayment` INTEGER, `needsProcessing` INTEGER, `giftCardCode` TEXT NOT NULL DEFAULT '', `giftCardAmount` TEXT NOT NULL DEFAULT '', `shippingTax` TEXT NOT NULL DEFAULT '', `createdVia` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`localSiteId`, `orderId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "orderKey", + "columnName": "orderKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateModified", + "columnName": "dateModified", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "totalTax", + "columnName": "totalTax", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingTotal", + "columnName": "shippingTotal", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentMethod", + "columnName": "paymentMethod", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentMethodTitle", + "columnName": "paymentMethodTitle", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "datePaid", + "columnName": "datePaid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pricesIncludeTax", + "columnName": "pricesIncludeTax", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customerNote", + "columnName": "customerNote", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "discountTotal", + "columnName": "discountTotal", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "discountCodes", + "columnName": "discountCodes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "refundTotal", + "columnName": "refundTotal", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "customerId", + "columnName": "customerId", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "billingFirstName", + "columnName": "billingFirstName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingLastName", + "columnName": "billingLastName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingCompany", + "columnName": "billingCompany", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingAddress1", + "columnName": "billingAddress1", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingAddress2", + "columnName": "billingAddress2", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingCity", + "columnName": "billingCity", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingState", + "columnName": "billingState", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingPostcode", + "columnName": "billingPostcode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingCountry", + "columnName": "billingCountry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingEmail", + "columnName": "billingEmail", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingPhone", + "columnName": "billingPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingFirstName", + "columnName": "shippingFirstName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingLastName", + "columnName": "shippingLastName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingCompany", + "columnName": "shippingCompany", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingAddress1", + "columnName": "shippingAddress1", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingAddress2", + "columnName": "shippingAddress2", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingCity", + "columnName": "shippingCity", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingState", + "columnName": "shippingState", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingPostcode", + "columnName": "shippingPostcode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingCountry", + "columnName": "shippingCountry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingPhone", + "columnName": "shippingPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lineItems", + "columnName": "lineItems", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingLines", + "columnName": "shippingLines", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "feeLines", + "columnName": "feeLines", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "taxLines", + "columnName": "taxLines", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "couponLines", + "columnName": "couponLines", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "metaData", + "columnName": "metaData", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentUrl", + "columnName": "paymentUrl", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "isEditable", + "columnName": "isEditable", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "needsPayment", + "columnName": "needsPayment", + "affinity": "INTEGER" + }, + { + "fieldPath": "needsProcessing", + "columnName": "needsProcessing", + "affinity": "INTEGER" + }, + { + "fieldPath": "giftCardCode", + "columnName": "giftCardCode", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "giftCardAmount", + "columnName": "giftCardAmount", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "shippingTax", + "columnName": "shippingTax", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "createdVia", + "columnName": "createdVia", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "orderId" + ] + }, + "indices": [ + { + "name": "index_OrderEntity_localSiteId_orderId", + "unique": false, + "columnNames": [ + "localSiteId", + "orderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_OrderEntity_localSiteId_orderId` ON `${TABLE_NAME}` (`localSiteId`, `orderId`)" + } + ] + }, + { + "tableName": "RefundEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteId` INTEGER NOT NULL, `orderId` INTEGER NOT NULL, `refundId` INTEGER NOT NULL, `data` TEXT NOT NULL, PRIMARY KEY(`siteId`, `orderId`, `refundId`))", + "fields": [ + { + "fieldPath": "siteId", + "columnName": "siteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "refundId", + "columnName": "refundId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "siteId", + "orderId", + "refundId" + ] + } + }, + { + "tableName": "MetaData", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `id` INTEGER NOT NULL, `parentItemId` INTEGER NOT NULL, `key` TEXT NOT NULL, `value` TEXT NOT NULL, `type` TEXT NOT NULL DEFAULT 'ORDER', PRIMARY KEY(`localSiteId`, `parentItemId`, `id`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "parentItemId", + "columnName": "parentItemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'ORDER'" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "parentItemId", + "id" + ] + } + }, + { + "tableName": "InboxNotes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `remoteId` INTEGER NOT NULL, `localSiteId` INTEGER NOT NULL, `name` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `dateCreated` TEXT NOT NULL, `status` TEXT NOT NULL, `source` TEXT, `type` TEXT, `dateReminder` TEXT)", + "fields": [ + { + "fieldPath": "localId", + "columnName": "localId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT" + }, + { + "fieldPath": "dateReminder", + "columnName": "dateReminder", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "localId" + ] + }, + "indices": [ + { + "name": "index_InboxNotes_remoteId_localSiteId", + "unique": true, + "columnNames": [ + "remoteId", + "localSiteId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_InboxNotes_remoteId_localSiteId` ON `${TABLE_NAME}` (`remoteId`, `localSiteId`)" + } + ] + }, + { + "tableName": "InboxNoteActions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`remoteId` INTEGER NOT NULL, `inboxNoteLocalId` INTEGER NOT NULL, `localSiteId` INTEGER NOT NULL, `name` TEXT NOT NULL, `label` TEXT NOT NULL, `url` TEXT NOT NULL, `query` TEXT, `status` TEXT, `primary` INTEGER NOT NULL, `actionedText` TEXT, PRIMARY KEY(`remoteId`, `inboxNoteLocalId`), FOREIGN KEY(`inboxNoteLocalId`) REFERENCES `InboxNotes`(`localId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inboxNoteLocalId", + "columnName": "inboxNoteLocalId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "query", + "columnName": "query", + "affinity": "TEXT" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT" + }, + { + "fieldPath": "primary", + "columnName": "primary", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "actionedText", + "columnName": "actionedText", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "remoteId", + "inboxNoteLocalId" + ] + }, + "indices": [ + { + "name": "index_InboxNoteActions_inboxNoteLocalId", + "unique": false, + "columnNames": [ + "inboxNoteLocalId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_InboxNoteActions_inboxNoteLocalId` ON `${TABLE_NAME}` (`inboxNoteLocalId`)" + } + ], + "foreignKeys": [ + { + "table": "InboxNotes", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "inboxNoteLocalId" + ], + "referencedColumns": [ + "localId" + ] + } + ] + }, + { + "tableName": "TopPerformerProducts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `datePeriod` TEXT NOT NULL, `productId` INTEGER NOT NULL, `name` TEXT NOT NULL, `imageUrl` TEXT, `quantity` INTEGER NOT NULL, `currency` TEXT NOT NULL, `total` REAL NOT NULL, `millisSinceLastUpdated` INTEGER NOT NULL, PRIMARY KEY(`datePeriod`, `productId`, `localSiteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "datePeriod", + "columnName": "datePeriod", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "quantity", + "columnName": "quantity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "millisSinceLastUpdated", + "columnName": "millisSinceLastUpdated", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "datePeriod", + "productId", + "localSiteId" + ] + } + }, + { + "tableName": "TaxBasedOnSetting", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `selectedOption` TEXT NOT NULL, PRIMARY KEY(`localSiteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "selectedOption", + "columnName": "selectedOption", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId" + ] + } + }, + { + "tableName": "TaxRate", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `localSiteId` INTEGER NOT NULL, `country` TEXT, `state` TEXT, `postcode` TEXT, `city` TEXT, `rate` TEXT, `name` TEXT, `taxClass` TEXT, PRIMARY KEY(`id`, `localSiteId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "country", + "columnName": "country", + "affinity": "TEXT" + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT" + }, + { + "fieldPath": "postcode", + "columnName": "postcode", + "affinity": "TEXT" + }, + { + "fieldPath": "city", + "columnName": "city", + "affinity": "TEXT" + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "TEXT" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT" + }, + { + "fieldPath": "taxClass", + "columnName": "taxClass", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id", + "localSiteId" + ] + } + }, + { + "tableName": "WooPaymentsDepositsOverview", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `depositsEnabled` INTEGER, `depositsBlocked` INTEGER, `defaultCurrency` TEXT, `delayDays` INTEGER, `weeklyAnchor` TEXT, `monthlyAnchor` INTEGER, `interval` TEXT, PRIMARY KEY(`localSiteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "account.depositsEnabled", + "columnName": "depositsEnabled", + "affinity": "INTEGER" + }, + { + "fieldPath": "account.depositsBlocked", + "columnName": "depositsBlocked", + "affinity": "INTEGER" + }, + { + "fieldPath": "account.defaultCurrency", + "columnName": "defaultCurrency", + "affinity": "TEXT" + }, + { + "fieldPath": "account.depositsSchedule.delayDays", + "columnName": "delayDays", + "affinity": "INTEGER" + }, + { + "fieldPath": "account.depositsSchedule.weeklyAnchor", + "columnName": "weeklyAnchor", + "affinity": "TEXT" + }, + { + "fieldPath": "account.depositsSchedule.monthlyAnchor", + "columnName": "monthlyAnchor", + "affinity": "INTEGER" + }, + { + "fieldPath": "account.depositsSchedule.interval", + "columnName": "interval", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId" + ] + } + }, + { + "tableName": "WooPaymentsDeposits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `localSiteId` INTEGER NOT NULL, `depositId` TEXT, `date` INTEGER, `type` TEXT, `amount` INTEGER, `status` TEXT, `bankAccount` TEXT, `currency` TEXT, `automatic` INTEGER, `fee` INTEGER, `feePercentage` REAL, `created` INTEGER, `depositType` TEXT NOT NULL, FOREIGN KEY(`localSiteId`) REFERENCES `WooPaymentsDepositsOverview`(`localSiteId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "depositId", + "columnName": "depositId", + "affinity": "TEXT" + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT" + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT" + }, + { + "fieldPath": "bankAccount", + "columnName": "bankAccount", + "affinity": "TEXT" + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT" + }, + { + "fieldPath": "automatic", + "columnName": "automatic", + "affinity": "INTEGER" + }, + { + "fieldPath": "fee", + "columnName": "fee", + "affinity": "INTEGER" + }, + { + "fieldPath": "feePercentage", + "columnName": "feePercentage", + "affinity": "REAL" + }, + { + "fieldPath": "created", + "columnName": "created", + "affinity": "INTEGER" + }, + { + "fieldPath": "depositType", + "columnName": "depositType", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "foreignKeys": [ + { + "table": "WooPaymentsDepositsOverview", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "localSiteId" + ], + "referencedColumns": [ + "localSiteId" + ] + } + ] + }, + { + "tableName": "WooPaymentsManualDeposits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `localSiteId` INTEGER NOT NULL, `currency` TEXT, `date` INTEGER, FOREIGN KEY(`localSiteId`) REFERENCES `WooPaymentsDepositsOverview`(`localSiteId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT" + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "foreignKeys": [ + { + "table": "WooPaymentsDepositsOverview", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "localSiteId" + ], + "referencedColumns": [ + "localSiteId" + ] + } + ] + }, + { + "tableName": "WooPaymentsBalance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `localSiteId` INTEGER NOT NULL, `amount` INTEGER, `currency` TEXT, `fee` INTEGER, `feePercentage` REAL, `net` INTEGER, `balanceType` TEXT NOT NULL, `card` INTEGER, FOREIGN KEY(`localSiteId`) REFERENCES `WooPaymentsDepositsOverview`(`localSiteId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER" + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT" + }, + { + "fieldPath": "fee", + "columnName": "fee", + "affinity": "INTEGER" + }, + { + "fieldPath": "feePercentage", + "columnName": "feePercentage", + "affinity": "REAL" + }, + { + "fieldPath": "net", + "columnName": "net", + "affinity": "INTEGER" + }, + { + "fieldPath": "balanceType", + "columnName": "balanceType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sourceTypes.card", + "columnName": "card", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "foreignKeys": [ + { + "table": "WooPaymentsDepositsOverview", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "localSiteId" + ], + "referencedColumns": [ + "localSiteId" + ] + } + ] + }, + { + "tableName": "VisitorSummaryStatsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `date` TEXT NOT NULL, `granularity` TEXT NOT NULL, `views` INTEGER NOT NULL, `visitors` INTEGER NOT NULL, PRIMARY KEY(`localSiteId`, `date`, `granularity`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "granularity", + "columnName": "granularity", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "views", + "columnName": "views", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "visitors", + "columnName": "visitors", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "date", + "granularity" + ] + } + }, + { + "tableName": "ShippingMethod", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `localSiteId` INTEGER NOT NULL, `title` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "id" + ] + } + }, + { + "tableName": "CustomerFromAnalytics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `id` INTEGER NOT NULL, `userId` INTEGER NOT NULL, `avgOrderValue` REAL NOT NULL, `city` TEXT NOT NULL, `country` TEXT NOT NULL, `dateLastActive` TEXT NOT NULL, `dateLastActiveGmt` TEXT NOT NULL, `dateLastOrder` TEXT NOT NULL, `dateRegistered` TEXT NOT NULL, `dateRegisteredGmt` TEXT NOT NULL, `email` TEXT NOT NULL, `name` TEXT NOT NULL, `ordersCount` INTEGER NOT NULL, `postcode` TEXT NOT NULL, `state` TEXT NOT NULL, `totalSpend` REAL NOT NULL, `username` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `id`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "avgOrderValue", + "columnName": "avgOrderValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "city", + "columnName": "city", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "country", + "columnName": "country", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateLastActive", + "columnName": "dateLastActive", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateLastActiveGmt", + "columnName": "dateLastActiveGmt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateLastOrder", + "columnName": "dateLastOrder", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateRegistered", + "columnName": "dateRegistered", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateRegisteredGmt", + "columnName": "dateRegisteredGmt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ordersCount", + "columnName": "ordersCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "postcode", + "columnName": "postcode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "totalSpend", + "columnName": "totalSpend", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "id" + ] + } + }, + { + "tableName": "ProductEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `remoteId` INTEGER NOT NULL, `name` TEXT NOT NULL, `slug` TEXT NOT NULL, `permalink` TEXT NOT NULL, `dateCreated` TEXT NOT NULL, `dateModified` TEXT NOT NULL, `type` TEXT NOT NULL, `status` TEXT NOT NULL, `featured` INTEGER NOT NULL, `catalogVisibility` TEXT NOT NULL, `description` TEXT NOT NULL, `shortDescription` TEXT NOT NULL, `sku` TEXT NOT NULL, `globalUniqueId` TEXT NOT NULL, `price` TEXT NOT NULL, `regularPrice` TEXT NOT NULL, `salePrice` TEXT NOT NULL, `onSale` INTEGER NOT NULL, `totalSales` INTEGER NOT NULL, `purchasable` INTEGER NOT NULL, `dateOnSaleFrom` TEXT NOT NULL, `dateOnSaleTo` TEXT NOT NULL, `dateOnSaleFromGmt` TEXT NOT NULL, `dateOnSaleToGmt` TEXT NOT NULL, `virtual` INTEGER NOT NULL, `downloadable` INTEGER NOT NULL, `downloadLimit` INTEGER NOT NULL, `downloadExpiry` INTEGER NOT NULL, `soldIndividually` INTEGER NOT NULL, `externalUrl` TEXT NOT NULL, `buttonText` TEXT NOT NULL, `taxStatus` TEXT NOT NULL, `taxClass` TEXT NOT NULL, `manageStock` INTEGER NOT NULL, `stockQuantity` REAL NOT NULL, `stockStatus` TEXT NOT NULL, `backorders` TEXT NOT NULL, `backordersAllowed` INTEGER NOT NULL, `backordered` INTEGER NOT NULL, `shippingRequired` INTEGER NOT NULL, `shippingTaxable` INTEGER NOT NULL, `shippingClass` TEXT NOT NULL, `shippingClassId` INTEGER NOT NULL, `reviewsAllowed` INTEGER NOT NULL, `averageRating` TEXT NOT NULL, `ratingCount` INTEGER NOT NULL, `parentId` INTEGER NOT NULL, `purchaseNote` TEXT NOT NULL, `menuOrder` INTEGER NOT NULL, `categories` TEXT NOT NULL, `tags` TEXT NOT NULL, `images` TEXT NOT NULL, `attributes` TEXT NOT NULL, `variations` TEXT NOT NULL, `downloads` TEXT NOT NULL, `relatedIds` TEXT NOT NULL, `crossSellIds` TEXT NOT NULL, `upsellIds` TEXT NOT NULL, `groupedProductIds` TEXT NOT NULL, `weight` TEXT NOT NULL, `length` TEXT NOT NULL, `width` TEXT NOT NULL, `height` TEXT NOT NULL, `bundledItems` TEXT NOT NULL, `compositeComponents` TEXT NOT NULL, `specialStockStatus` TEXT NOT NULL, `bundleMinSize` REAL, `bundleMaxSize` REAL, `minAllowedQuantity` INTEGER NOT NULL, `maxAllowedQuantity` INTEGER NOT NULL, `groupOfQuantity` INTEGER NOT NULL, `combineVariationQuantities` INTEGER NOT NULL, `password` TEXT, `isSampleProduct` INTEGER NOT NULL, PRIMARY KEY(`localSiteId`, `remoteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "slug", + "columnName": "slug", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "permalink", + "columnName": "permalink", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateModified", + "columnName": "dateModified", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "featured", + "columnName": "featured", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "catalogVisibility", + "columnName": "catalogVisibility", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortDescription", + "columnName": "shortDescription", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sku", + "columnName": "sku", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "globalUniqueId", + "columnName": "globalUniqueId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "regularPrice", + "columnName": "regularPrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "salePrice", + "columnName": "salePrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "onSale", + "columnName": "onSale", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "totalSales", + "columnName": "totalSales", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "purchasable", + "columnName": "purchasable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateOnSaleFrom", + "columnName": "dateOnSaleFrom", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateOnSaleTo", + "columnName": "dateOnSaleTo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateOnSaleFromGmt", + "columnName": "dateOnSaleFromGmt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateOnSaleToGmt", + "columnName": "dateOnSaleToGmt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "virtual", + "columnName": "virtual", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloadable", + "columnName": "downloadable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloadLimit", + "columnName": "downloadLimit", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloadExpiry", + "columnName": "downloadExpiry", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "soldIndividually", + "columnName": "soldIndividually", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "externalUrl", + "columnName": "externalUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "buttonText", + "columnName": "buttonText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "taxStatus", + "columnName": "taxStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "taxClass", + "columnName": "taxClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "manageStock", + "columnName": "manageStock", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stockQuantity", + "columnName": "stockQuantity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "stockStatus", + "columnName": "stockStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "backorders", + "columnName": "backorders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "backordersAllowed", + "columnName": "backordersAllowed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "backordered", + "columnName": "backordered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shippingRequired", + "columnName": "shippingRequired", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shippingTaxable", + "columnName": "shippingTaxable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shippingClass", + "columnName": "shippingClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingClassId", + "columnName": "shippingClassId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reviewsAllowed", + "columnName": "reviewsAllowed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "averageRating", + "columnName": "averageRating", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ratingCount", + "columnName": "ratingCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "purchaseNote", + "columnName": "purchaseNote", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "menuOrder", + "columnName": "menuOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categories", + "columnName": "categories", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "images", + "columnName": "images", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "variations", + "columnName": "variations", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "downloads", + "columnName": "downloads", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "relatedIds", + "columnName": "relatedIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "crossSellIds", + "columnName": "crossSellIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "upsellIds", + "columnName": "upsellIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "groupedProductIds", + "columnName": "groupedProductIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "length", + "columnName": "length", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "width", + "columnName": "width", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bundledItems", + "columnName": "bundledItems", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "compositeComponents", + "columnName": "compositeComponents", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "specialStockStatus", + "columnName": "specialStockStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bundleMinSize", + "columnName": "bundleMinSize", + "affinity": "REAL" + }, + { + "fieldPath": "bundleMaxSize", + "columnName": "bundleMaxSize", + "affinity": "REAL" + }, + { + "fieldPath": "minAllowedQuantity", + "columnName": "minAllowedQuantity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "maxAllowedQuantity", + "columnName": "maxAllowedQuantity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupOfQuantity", + "columnName": "groupOfQuantity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "combineVariationQuantities", + "columnName": "combineVariationQuantities", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT" + }, + { + "fieldPath": "isSampleProduct", + "columnName": "isSampleProduct", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "remoteId" + ] + } + }, + { + "tableName": "PosProductEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `remoteId` INTEGER NOT NULL, `name` TEXT NOT NULL, `sku` TEXT NOT NULL, `globalUniqueId` TEXT NOT NULL, `type` TEXT NOT NULL, `price` TEXT NOT NULL, `downloadable` INTEGER NOT NULL, `images` TEXT NOT NULL, `attributes` TEXT NOT NULL, `parentId` INTEGER, `status` TEXT NOT NULL, `regularPrice` TEXT NOT NULL, `salePrice` TEXT NOT NULL, `onSale` INTEGER NOT NULL, `description` TEXT NOT NULL, `shortDescription` TEXT NOT NULL, `manageStock` INTEGER NOT NULL, `stockQuantity` REAL, `stockStatus` TEXT NOT NULL, `backordersAllowed` INTEGER NOT NULL, `backordered` INTEGER NOT NULL, `categories` TEXT NOT NULL, `tags` TEXT NOT NULL, `dateModified` TEXT NOT NULL, `variations` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `remoteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sku", + "columnName": "sku", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "globalUniqueId", + "columnName": "globalUniqueId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "downloadable", + "columnName": "downloadable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "images", + "columnName": "images", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER" + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "regularPrice", + "columnName": "regularPrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "salePrice", + "columnName": "salePrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "onSale", + "columnName": "onSale", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortDescription", + "columnName": "shortDescription", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "manageStock", + "columnName": "manageStock", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stockQuantity", + "columnName": "stockQuantity", + "affinity": "REAL" + }, + { + "fieldPath": "stockStatus", + "columnName": "stockStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "backordersAllowed", + "columnName": "backordersAllowed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "backordered", + "columnName": "backordered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categories", + "columnName": "categories", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateModified", + "columnName": "dateModified", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "variations", + "columnName": "variations", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "remoteId" + ] + } + }, + { + "tableName": "PosVariationEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `remoteProductId` INTEGER NOT NULL, `remoteVariationId` INTEGER NOT NULL, `dateModified` TEXT NOT NULL, `sku` TEXT NOT NULL, `globalUniqueId` TEXT NOT NULL, `variationName` TEXT NOT NULL, `price` TEXT NOT NULL, `regularPrice` TEXT NOT NULL, `salePrice` TEXT NOT NULL, `description` TEXT NOT NULL, `stockQuantity` REAL NOT NULL, `stockStatus` TEXT NOT NULL, `manageStock` INTEGER NOT NULL, `backordered` INTEGER NOT NULL, `attributesJson` TEXT NOT NULL, `imageUrl` TEXT NOT NULL, `status` TEXT NOT NULL, `lastUpdated` TEXT NOT NULL, `downloadable` INTEGER NOT NULL, PRIMARY KEY(`localSiteId`, `remoteProductId`, `remoteVariationId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteProductId", + "columnName": "remoteProductId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteVariationId", + "columnName": "remoteVariationId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateModified", + "columnName": "dateModified", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sku", + "columnName": "sku", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "globalUniqueId", + "columnName": "globalUniqueId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "variationName", + "columnName": "variationName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "regularPrice", + "columnName": "regularPrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "salePrice", + "columnName": "salePrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stockQuantity", + "columnName": "stockQuantity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "stockStatus", + "columnName": "stockStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "manageStock", + "columnName": "manageStock", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "backordered", + "columnName": "backordered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "attributesJson", + "columnName": "attributesJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastUpdated", + "columnName": "lastUpdated", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "downloadable", + "columnName": "downloadable", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "remoteProductId", + "remoteVariationId" + ] + } + }, + { + "tableName": "ProductCategoryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `remoteCategoryId` INTEGER NOT NULL, `name` TEXT NOT NULL, `slug` TEXT NOT NULL, `parent` INTEGER NOT NULL, PRIMARY KEY(`localSiteId`, `remoteCategoryId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteCategoryId", + "columnName": "remoteCategoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "slug", + "columnName": "slug", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "remoteCategoryId" + ] + } + }, + { + "tableName": "ProductVariationEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `remoteProductId` INTEGER NOT NULL, `remoteVariationId` INTEGER NOT NULL, `dateCreated` TEXT NOT NULL, `dateModified` TEXT NOT NULL, `description` TEXT NOT NULL, `permalink` TEXT NOT NULL, `sku` TEXT NOT NULL, `globalUniqueId` TEXT NOT NULL, `status` TEXT NOT NULL, `price` TEXT NOT NULL, `regularPrice` TEXT NOT NULL, `salePrice` TEXT NOT NULL, `dateOnSaleFrom` TEXT NOT NULL, `dateOnSaleTo` TEXT NOT NULL, `dateOnSaleFromGmt` TEXT NOT NULL, `dateOnSaleToGmt` TEXT NOT NULL, `taxStatus` TEXT NOT NULL, `taxClass` TEXT NOT NULL, `onSale` INTEGER NOT NULL, `purchasable` INTEGER NOT NULL, `virtual` INTEGER NOT NULL, `downloadable` INTEGER NOT NULL, `downloadLimit` INTEGER NOT NULL, `downloadExpiry` INTEGER NOT NULL, `downloads` TEXT NOT NULL, `backorders` TEXT NOT NULL, `backordersAllowed` INTEGER NOT NULL, `backordered` INTEGER NOT NULL, `shippingClass` TEXT NOT NULL, `shippingClassId` INTEGER NOT NULL, `manageStock` INTEGER NOT NULL, `stockQuantity` REAL NOT NULL, `stockStatus` TEXT NOT NULL, `image` TEXT NOT NULL, `weight` TEXT NOT NULL, `length` TEXT NOT NULL, `width` TEXT NOT NULL, `height` TEXT NOT NULL, `minAllowedQuantity` INTEGER NOT NULL, `maxAllowedQuantity` INTEGER NOT NULL, `groupOfQuantity` INTEGER NOT NULL, `overrideProductQuantities` INTEGER NOT NULL, `menuOrder` INTEGER NOT NULL, `attributes` TEXT NOT NULL, `metadata` TEXT, PRIMARY KEY(`localSiteId`, `remoteProductId`, `remoteVariationId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteProductId", + "columnName": "remoteProductId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteVariationId", + "columnName": "remoteVariationId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateModified", + "columnName": "dateModified", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "permalink", + "columnName": "permalink", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sku", + "columnName": "sku", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "globalUniqueId", + "columnName": "globalUniqueId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "regularPrice", + "columnName": "regularPrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "salePrice", + "columnName": "salePrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateOnSaleFrom", + "columnName": "dateOnSaleFrom", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateOnSaleTo", + "columnName": "dateOnSaleTo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateOnSaleFromGmt", + "columnName": "dateOnSaleFromGmt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateOnSaleToGmt", + "columnName": "dateOnSaleToGmt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "taxStatus", + "columnName": "taxStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "taxClass", + "columnName": "taxClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "onSale", + "columnName": "onSale", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "purchasable", + "columnName": "purchasable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "virtual", + "columnName": "virtual", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloadable", + "columnName": "downloadable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloadLimit", + "columnName": "downloadLimit", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloadExpiry", + "columnName": "downloadExpiry", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloads", + "columnName": "downloads", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "backorders", + "columnName": "backorders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "backordersAllowed", + "columnName": "backordersAllowed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "backordered", + "columnName": "backordered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shippingClass", + "columnName": "shippingClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingClassId", + "columnName": "shippingClassId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "manageStock", + "columnName": "manageStock", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stockQuantity", + "columnName": "stockQuantity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "stockStatus", + "columnName": "stockStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "length", + "columnName": "length", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "width", + "columnName": "width", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "minAllowedQuantity", + "columnName": "minAllowedQuantity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "maxAllowedQuantity", + "columnName": "maxAllowedQuantity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupOfQuantity", + "columnName": "groupOfQuantity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "overrideProductQuantities", + "columnName": "overrideProductQuantities", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "menuOrder", + "columnName": "menuOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "attributes", + "columnName": "attributes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "metadata", + "columnName": "metadata", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "remoteProductId", + "remoteVariationId" + ] + } + }, + { + "tableName": "ProductTagEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `remoteTagId` INTEGER NOT NULL, `name` TEXT NOT NULL, `slug` TEXT NOT NULL, `description` TEXT NOT NULL, `count` INTEGER NOT NULL, PRIMARY KEY(`localSiteId`, `remoteTagId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteTagId", + "columnName": "remoteTagId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "slug", + "columnName": "slug", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "count", + "columnName": "count", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "remoteTagId" + ] + } + }, + { + "tableName": "ProductShippingClassEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `remoteShippingClassId` INTEGER NOT NULL, `name` TEXT NOT NULL, `slug` TEXT NOT NULL, `description` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `remoteShippingClassId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteShippingClassId", + "columnName": "remoteShippingClassId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "slug", + "columnName": "slug", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "remoteShippingClassId" + ] + } + }, + { + "tableName": "ProductReviewEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `remoteProductReviewId` INTEGER NOT NULL, `remoteProductId` INTEGER NOT NULL, `dateCreated` TEXT NOT NULL, `status` TEXT NOT NULL, `reviewerName` TEXT NOT NULL, `reviewerEmail` TEXT NOT NULL, `review` TEXT NOT NULL, `rating` INTEGER NOT NULL, `verified` INTEGER NOT NULL, `reviewerAvatarsJson` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `remoteProductReviewId`, `remoteProductId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteProductReviewId", + "columnName": "remoteProductReviewId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteProductId", + "columnName": "remoteProductId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "reviewerName", + "columnName": "reviewerName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "reviewerEmail", + "columnName": "reviewerEmail", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "review", + "columnName": "review", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rating", + "columnName": "rating", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "verified", + "columnName": "verified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reviewerAvatarsJson", + "columnName": "reviewerAvatarsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "remoteProductReviewId", + "remoteProductId" + ] + } + }, + { + "tableName": "ProductSettingsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `weightUnit` TEXT NOT NULL, `dimensionUnit` TEXT NOT NULL, PRIMARY KEY(`localSiteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weightUnit", + "columnName": "weightUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dimensionUnit", + "columnName": "dimensionUnit", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId" + ] + } + }, + { + "tableName": "CustomerEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `localSiteId` INTEGER NOT NULL, `remoteCustomerId` INTEGER NOT NULL, `avatarUrl` TEXT NOT NULL, `dateCreated` TEXT NOT NULL, `dateCreatedGmt` TEXT NOT NULL, `dateModified` TEXT NOT NULL, `dateModifiedGmt` TEXT NOT NULL, `email` TEXT NOT NULL, `firstName` TEXT NOT NULL, `isPayingCustomer` INTEGER NOT NULL, `lastName` TEXT NOT NULL, `role` TEXT NOT NULL, `username` TEXT NOT NULL, `billingAddress1` TEXT NOT NULL, `billingAddress2` TEXT NOT NULL, `billingCity` TEXT NOT NULL, `billingCompany` TEXT NOT NULL, `billingCountry` TEXT NOT NULL, `billingEmail` TEXT NOT NULL, `billingFirstName` TEXT NOT NULL, `billingLastName` TEXT NOT NULL, `billingPhone` TEXT NOT NULL, `billingPostcode` TEXT NOT NULL, `billingState` TEXT NOT NULL, `shippingAddress1` TEXT NOT NULL, `shippingAddress2` TEXT NOT NULL, `shippingCity` TEXT NOT NULL, `shippingCompany` TEXT NOT NULL, `shippingCountry` TEXT NOT NULL, `shippingFirstName` TEXT NOT NULL, `shippingLastName` TEXT NOT NULL, `shippingPostcode` TEXT NOT NULL, `shippingState` TEXT NOT NULL, `analyticsCustomerId` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteCustomerId", + "columnName": "remoteCustomerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateCreatedGmt", + "columnName": "dateCreatedGmt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateModified", + "columnName": "dateModified", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateModifiedGmt", + "columnName": "dateModifiedGmt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "firstName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isPayingCustomer", + "columnName": "isPayingCustomer", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastName", + "columnName": "lastName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingAddress1", + "columnName": "billingAddress1", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingAddress2", + "columnName": "billingAddress2", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingCity", + "columnName": "billingCity", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingCompany", + "columnName": "billingCompany", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingCountry", + "columnName": "billingCountry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingEmail", + "columnName": "billingEmail", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingFirstName", + "columnName": "billingFirstName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingLastName", + "columnName": "billingLastName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingPhone", + "columnName": "billingPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingPostcode", + "columnName": "billingPostcode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "billingState", + "columnName": "billingState", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingAddress1", + "columnName": "shippingAddress1", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingAddress2", + "columnName": "shippingAddress2", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingCity", + "columnName": "shippingCity", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingCompany", + "columnName": "shippingCompany", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingCountry", + "columnName": "shippingCountry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingFirstName", + "columnName": "shippingFirstName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingLastName", + "columnName": "shippingLastName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingPostcode", + "columnName": "shippingPostcode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shippingState", + "columnName": "shippingState", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsCustomerId", + "columnName": "analyticsCustomerId", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "LocationEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`parentCode` TEXT NOT NULL, `code` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`parentCode`, `code`))", + "fields": [ + { + "fieldPath": "parentCode", + "columnName": "parentCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentCode", + "code" + ] + } + }, + { + "tableName": "OrderShipmentProviderEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `country` TEXT NOT NULL, `carrierName` TEXT NOT NULL, `carrierLink` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `carrierName`, `country`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "country", + "columnName": "country", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "carrierName", + "columnName": "carrierName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "carrierLink", + "columnName": "carrierLink", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "carrierName", + "country" + ] + } + }, + { + "tableName": "UserEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `remoteUserId` INTEGER NOT NULL, `firstName` TEXT NOT NULL, `lastName` TEXT NOT NULL, `username` TEXT NOT NULL, `email` TEXT NOT NULL, `roles` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `remoteUserId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteUserId", + "columnName": "remoteUserId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "firstName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastName", + "columnName": "lastName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "remoteUserId" + ] + } + }, + { + "tableName": "TaxClassEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `name` TEXT NOT NULL, `slug` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `slug`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "slug", + "columnName": "slug", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "slug" + ] + } + }, + { + "tableName": "SettingsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `currencyCode` TEXT NOT NULL, `currencyPosition` TEXT NOT NULL, `currencyThousandSeparator` TEXT NOT NULL, `currencyDecimalSeparator` TEXT NOT NULL, `currencyDecimalNumber` INTEGER NOT NULL, `countryCode` TEXT NOT NULL, `stateCode` TEXT NOT NULL, `address` TEXT NOT NULL, `address2` TEXT NOT NULL, `city` TEXT NOT NULL, `postalCode` TEXT NOT NULL, `couponsEnabled` INTEGER NOT NULL, PRIMARY KEY(`localSiteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "currencyCode", + "columnName": "currencyCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currencyPosition", + "columnName": "currencyPosition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currencyThousandSeparator", + "columnName": "currencyThousandSeparator", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currencyDecimalSeparator", + "columnName": "currencyDecimalSeparator", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currencyDecimalNumber", + "columnName": "currencyDecimalNumber", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "countryCode", + "columnName": "countryCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stateCode", + "columnName": "stateCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address2", + "columnName": "address2", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "city", + "columnName": "city", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "postalCode", + "columnName": "postalCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "couponsEnabled", + "columnName": "couponsEnabled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId" + ] + } + }, + { + "tableName": "OrderSummaryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteId` INTEGER NOT NULL, `orderId` INTEGER NOT NULL, `dateCreated` TEXT NOT NULL, PRIMARY KEY(`siteId`, `orderId`))", + "fields": [ + { + "fieldPath": "siteId", + "columnName": "siteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "siteId", + "orderId" + ] + } + }, + { + "tableName": "OrderStatusEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteId` INTEGER NOT NULL, `statusKey` TEXT NOT NULL, `label` TEXT NOT NULL, `statusCount` INTEGER NOT NULL, `position` INTEGER NOT NULL, PRIMARY KEY(`siteId`, `statusKey`))", + "fields": [ + { + "fieldPath": "siteId", + "columnName": "siteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "statusKey", + "columnName": "statusKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "statusCount", + "columnName": "statusCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "siteId", + "statusKey" + ] + } + }, + { + "tableName": "WooShippingLabelEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `orderId` INTEGER NOT NULL, `labelId` INTEGER NOT NULL, `tracking` TEXT NOT NULL, `refundableAmount` TEXT NOT NULL, `status` TEXT NOT NULL, `created` TEXT, `carrierId` TEXT NOT NULL, `serviceName` TEXT NOT NULL, `commercialInvoiceUrl` TEXT, `isCommercialInvoiceSubmittedElectronically` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isLetter` INTEGER NOT NULL, `productNames` TEXT NOT NULL, `productIds` TEXT, `shipmentId` TEXT, `receiptItemId` INTEGER NOT NULL, `createdDate` TEXT, `mainReceiptId` INTEGER NOT NULL, `rate` TEXT NOT NULL, `currency` TEXT NOT NULL, `expiryDate` INTEGER NOT NULL, `usedDate` INTEGER, `refund` TEXT, `hazmatCategory` TEXT, `originAddress` TEXT, `destinationAddress` TEXT, PRIMARY KEY(`localSiteId`, `orderId`, `labelId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "labelId", + "columnName": "labelId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tracking", + "columnName": "tracking", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "refundableAmount", + "columnName": "refundableAmount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "created", + "columnName": "created", + "affinity": "TEXT" + }, + { + "fieldPath": "carrierId", + "columnName": "carrierId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serviceName", + "columnName": "serviceName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "commercialInvoiceUrl", + "columnName": "commercialInvoiceUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "isCommercialInvoiceSubmittedElectronically", + "columnName": "isCommercialInvoiceSubmittedElectronically", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isLetter", + "columnName": "isLetter", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productNames", + "columnName": "productNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "productIds", + "columnName": "productIds", + "affinity": "TEXT" + }, + { + "fieldPath": "shipmentId", + "columnName": "shipmentId", + "affinity": "TEXT" + }, + { + "fieldPath": "receiptItemId", + "columnName": "receiptItemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdDate", + "columnName": "createdDate", + "affinity": "TEXT" + }, + { + "fieldPath": "mainReceiptId", + "columnName": "mainReceiptId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expiryDate", + "columnName": "expiryDate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "usedDate", + "columnName": "usedDate", + "affinity": "INTEGER" + }, + { + "fieldPath": "refund", + "columnName": "refund", + "affinity": "TEXT" + }, + { + "fieldPath": "hazmatCategory", + "columnName": "hazmatCategory", + "affinity": "TEXT" + }, + { + "fieldPath": "originAddress", + "columnName": "originAddress", + "affinity": "TEXT" + }, + { + "fieldPath": "destinationAddress", + "columnName": "destinationAddress", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "orderId", + "labelId" + ] + } + }, + { + "tableName": "WooShippingShipmentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `orderId` INTEGER NOT NULL, `shipmentId` TEXT NOT NULL, `items` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `orderId`, `shipmentId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shipmentId", + "columnName": "shipmentId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "items", + "columnName": "items", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "orderId", + "shipmentId" + ] + } + }, + { + "tableName": "WooShippingPackagesEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `storeOptions` TEXT NOT NULL, `savedPackages` TEXT NOT NULL, `carrierPackageGroups` TEXT NOT NULL, PRIMARY KEY(`localSiteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "storeOptions", + "columnName": "storeOptions", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "savedPackages", + "columnName": "savedPackages", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "carrierPackageGroups", + "columnName": "carrierPackageGroups", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId" + ] + } + }, + { + "tableName": "GlobalAttributeEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteId` INTEGER NOT NULL, `remoteId` INTEGER NOT NULL, `name` TEXT NOT NULL, `slug` TEXT NOT NULL, `type` TEXT NOT NULL, `orderBy` TEXT NOT NULL, `hasArchives` INTEGER NOT NULL, PRIMARY KEY(`siteId`, `remoteId`))", + "fields": [ + { + "fieldPath": "siteId", + "columnName": "siteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "slug", + "columnName": "slug", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "orderBy", + "columnName": "orderBy", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasArchives", + "columnName": "hasArchives", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "siteId", + "remoteId" + ] + } + }, + { + "tableName": "GatewayEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteId` INTEGER NOT NULL, `gatewayId` TEXT NOT NULL, `data` TEXT NOT NULL, PRIMARY KEY(`siteId`, `gatewayId`))", + "fields": [ + { + "fieldPath": "siteId", + "columnName": "siteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "gatewayId", + "columnName": "gatewayId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "siteId", + "gatewayId" + ] + } + }, + { + "tableName": "NewVisitorStatsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `granularity` TEXT NOT NULL, `date` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `quantity` TEXT NOT NULL, `fields` TEXT NOT NULL, `data` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `granularity`, `date`, `quantity`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "granularity", + "columnName": "granularity", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "startDate", + "columnName": "startDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endDate", + "columnName": "endDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "quantity", + "columnName": "quantity", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fields", + "columnName": "fields", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "granularity", + "date", + "quantity" + ] + } + }, + { + "tableName": "Bookings", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `localSiteId` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `allDay` INTEGER NOT NULL, `status` TEXT NOT NULL, `cost` TEXT NOT NULL, `currency` TEXT NOT NULL, `customerId` INTEGER NOT NULL, `productId` INTEGER NOT NULL, `resourceId` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `dateModified` INTEGER NOT NULL, `googleCalendarEventId` TEXT NOT NULL, `orderId` INTEGER NOT NULL, `orderItemId` INTEGER NOT NULL, `parentId` INTEGER NOT NULL, `personCounts` TEXT, `localTimezone` TEXT NOT NULL, PRIMARY KEY(`id`, `localSiteId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "allDay", + "columnName": "allDay", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cost", + "columnName": "cost", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "customerId", + "columnName": "customerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "resourceId", + "columnName": "resourceId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateModified", + "columnName": "dateModified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "googleCalendarEventId", + "columnName": "googleCalendarEventId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderItemId", + "columnName": "orderItemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "personCounts", + "columnName": "personCounts", + "affinity": "TEXT" + }, + { + "fieldPath": "localTimezone", + "columnName": "localTimezone", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id", + "localSiteId" + ] + } + }, + { + "tableName": "RevenueStatsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `interval` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `data` TEXT NOT NULL, `total` TEXT NOT NULL, `rangeId` TEXT NOT NULL, PRIMARY KEY(`localSiteId`, `rangeId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interval", + "columnName": "interval", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "startDate", + "columnName": "startDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endDate", + "columnName": "endDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rangeId", + "columnName": "rangeId", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "localSiteId", + "rangeId" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '116ada8d6e7616a6f6fffe7229924e8b')" + ] + } +} \ No newline at end of file diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/di/WCDatabaseModule.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/di/WCDatabaseModule.kt index daef770842f5..a8ca620e6c4f 100644 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/di/WCDatabaseModule.kt +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/di/WCDatabaseModule.kt @@ -10,6 +10,7 @@ import org.wordpress.android.fluxc.persistence.ProductSqlUtils import org.wordpress.android.fluxc.persistence.TransactionExecutor import org.wordpress.android.fluxc.persistence.WCAndroidDatabase import org.wordpress.android.fluxc.persistence.converters.CurrencyPositionConverter +import org.wordpress.android.fluxc.persistence.converters.StatsGranularityConverter import org.wordpress.android.fluxc.persistence.dao.AddonsDao import org.wordpress.android.fluxc.persistence.dao.CouponsDao import org.wordpress.android.fluxc.persistence.dao.CustomerFromAnalyticsDao @@ -27,8 +28,16 @@ interface WCDatabaseModule { companion object { @Singleton @Provides - fun provideDatabase(context: Context, currencyPositionConverter: CurrencyPositionConverter): WCAndroidDatabase { - return WCAndroidDatabase.buildDb(context, currencyPositionConverter) + fun provideDatabase( + context: Context, + currencyPositionConverter: CurrencyPositionConverter, + statsGranularityConverter: StatsGranularityConverter, + ): WCAndroidDatabase { + return WCAndroidDatabase.buildDb( + context, + currencyPositionConverter, + statsGranularityConverter, + ) } @Provides internal fun provideAddonsDao(database: WCAndroidDatabase): AddonsDao { @@ -124,6 +133,8 @@ interface WCDatabaseModule { @Provides internal fun provideNewVisitorStatsDao(database: WCAndroidDatabase) = database.newVisitorStatsDao @Provides internal fun provideBookingsDao(database: WCAndroidDatabase) = database.bookingsDao + + @Provides internal fun provideRevenueStatsDao(database: WCAndroidDatabase) = database.revenueStatsDao } @Binds fun bindTransactionExecutor(database: WCAndroidDatabase): TransactionExecutor } diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/model/WCRevenueStatsModel.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/model/WCRevenueStatsModel.kt index 43f64fbbc203..81a2f8a37dbf 100644 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/model/WCRevenueStatsModel.kt +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/model/WCRevenueStatsModel.kt @@ -1,37 +1,31 @@ package org.wordpress.android.fluxc.model +import androidx.room.Entity import com.google.gson.Gson import com.google.gson.JsonParser import com.google.gson.annotations.JsonAdapter import com.google.gson.annotations.SerializedName import com.google.gson.reflect.TypeToken -import com.yarolegovich.wellsql.core.Identifiable -import com.yarolegovich.wellsql.core.annotation.Column -import com.yarolegovich.wellsql.core.annotation.PrimaryKey -import com.yarolegovich.wellsql.core.annotation.Table -import org.wordpress.android.fluxc.persistence.WellSqlConfig +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.utils.NonNegativeIntJsonDeserializer -@Table(addOn = WellSqlConfig.ADDON_WOOCOMMERCE) -data class WCRevenueStatsModel(@PrimaryKey @Column private var id: Int = 0) : Identifiable { - @Column var localSiteId = 0 - @Column var interval = "" // The unit ("hour", "day", "week", "month", "year") - @Column var startDate = "" // The start date of the data - @Column var endDate = "" // The end date of the data - @Column var data = "" // JSON - A list of lists; each nested list contains the data for a time period - @Column var total = "" // JSON - A map of total stats for a given time period - @Column var rangeId = "" // A ID for easily fetch the Revenue Stats for a given start and end date - +@Entity( + tableName = "RevenueStatsEntity", + primaryKeys = ["localSiteId", "rangeId"], +) +data class WCRevenueStatsModel( + val localSiteId: LocalId, + val interval: String, // The unit ("hour", "day", "week", "month", "year") + val startDate: String, // The start date of the data + val endDate: String, // The end date of the data + val data: String, // JSON - A list of lists; each nested list contains the data for a time period + val total: String, // JSON - A map of total stats for a given time period + val rangeId: String, // A ID for easily fetch the Revenue Stats for a given start and end date +) { companion object { private val gson by lazy { Gson() } } - override fun getId() = id - - override fun setId(id: Int) { - this.id = id - } - @Suppress("MemberNameEqualsClassName") class Interval { val interval: String? = null diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/orderstats/OrderStatsRestClient.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/orderstats/OrderStatsRestClient.kt index 53e7bb9cde51..e098f270bd69 100644 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/orderstats/OrderStatsRestClient.kt +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/orderstats/OrderStatsRestClient.kt @@ -70,6 +70,7 @@ class OrderStatsRestClient @Inject constructor( * Possible non-generic errors: * [OrderStatsErrorType.INVALID_PARAM] if [granularity], [startDate], or [endDate] are invalid or incompatible */ + @Suppress("LongParameterList") suspend fun fetchRevenueStats( site: SiteModel, granularity: StatsGranularity, @@ -77,7 +78,7 @@ class OrderStatsRestClient @Inject constructor( endDate: String, perPage: Int, forceRefresh: Boolean = false, - revenueRangeId: String = "", + revenueRangeId: String, dateType: String = "date_created", ): FetchRevenueStatsResponsePayload { val url = WOOCOMMERCE.reports.revenue.stats.pathV4Analytics @@ -103,15 +104,15 @@ class OrderStatsRestClient @Inject constructor( return when (response) { is WPAPIResponse.Success -> { response.data?.let { - val model = WCRevenueStatsModel().apply { - this.localSiteId = site.id - this.interval = granularity.toString() - this.data = it.intervals.toString() - this.total = it.totals.toString() - this.startDate = startDate - this.endDate = endDate - this.rangeId = revenueRangeId - } + val model = WCRevenueStatsModel( + localSiteId = site.localId(), + interval = granularity.toString(), + data = it.intervals.toString(), + total = it.totals.toString(), + startDate = startDate, + endDate = endDate, + rangeId = revenueRangeId, + ) FetchRevenueStatsResponsePayload(site, granularity, model) } ?: FetchRevenueStatsResponsePayload( @@ -161,12 +162,12 @@ class OrderStatsRestClient @Inject constructor( when (response) { is WPAPIResponse.Success -> { - val payload = FetchRevenueStatsAvailabilityResponsePayload(site, true) + val payload = FetchRevenueStatsAvailabilityResponsePayload(site) dispatcher.dispatch(WCStatsActionBuilder.newFetchedRevenueStatsAvailabilityAction(payload)) } is WPAPIResponse.Error -> { val orderError = response.error.toOrderError() - val payload = FetchRevenueStatsAvailabilityResponsePayload(orderError, site, false) + val payload = FetchRevenueStatsAvailabilityResponsePayload(orderError, site) dispatcher.dispatch(WCStatsActionBuilder.newFetchedRevenueStatsAvailabilityAction(payload)) } } diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/WCAndroidDatabase.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/WCAndroidDatabase.kt index 0b41e329c5e9..d8ac9b58761b 100644 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/WCAndroidDatabase.kt +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/WCAndroidDatabase.kt @@ -18,6 +18,7 @@ import org.wordpress.android.fluxc.model.WCProductSettingsModel import org.wordpress.android.fluxc.model.WCProductShippingClassModel import org.wordpress.android.fluxc.model.WCProductTagModel import org.wordpress.android.fluxc.model.WCProductVariationModel +import org.wordpress.android.fluxc.model.WCRevenueStatsModel import org.wordpress.android.fluxc.model.attribute.WCGlobalAttributeModel import org.wordpress.android.fluxc.model.customer.WCCustomerModel import org.wordpress.android.fluxc.model.data.WCLocationModel @@ -30,6 +31,7 @@ import org.wordpress.android.fluxc.persistence.converters.CurrencyPositionConver import org.wordpress.android.fluxc.persistence.converters.LocalIdConverter import org.wordpress.android.fluxc.persistence.converters.LongListConverter import org.wordpress.android.fluxc.persistence.converters.RemoteIdConverter +import org.wordpress.android.fluxc.persistence.converters.StatsGranularityConverter import org.wordpress.android.fluxc.persistence.converters.StringListConverter import org.wordpress.android.fluxc.persistence.dao.AddonsDao import org.wordpress.android.fluxc.persistence.dao.BookingsDao @@ -55,6 +57,7 @@ import org.wordpress.android.fluxc.persistence.dao.ProductTagsDao import org.wordpress.android.fluxc.persistence.dao.ProductVariationsDao import org.wordpress.android.fluxc.persistence.dao.ProductsDao import org.wordpress.android.fluxc.persistence.dao.RefundDao +import org.wordpress.android.fluxc.persistence.dao.RevenueStatsDao import org.wordpress.android.fluxc.persistence.dao.SettingsDao import org.wordpress.android.fluxc.persistence.dao.ShippingMethodDao import org.wordpress.android.fluxc.persistence.dao.TaxBasedOnDao @@ -122,7 +125,7 @@ import org.wordpress.android.fluxc.persistence.migrations.MIGRATION_7_8 import org.wordpress.android.fluxc.persistence.migrations.MIGRATION_8_9 import org.wordpress.android.fluxc.persistence.migrations.MIGRATION_9_10 -const val WC_DATABASE_VERSION = 66 +const val WC_DATABASE_VERSION = 67 @Database( version = WC_DATABASE_VERSION, @@ -172,6 +175,7 @@ const val WC_DATABASE_VERSION = 66 GatewayEntity::class, WCNewVisitorStatsModel::class, BookingEntity::class, + WCRevenueStatsModel::class, ], autoMigrations = [ AutoMigration(from = 12, to = 13), @@ -220,6 +224,7 @@ const val WC_DATABASE_VERSION = 66 AutoMigration(from = 63, to = 64), AutoMigration(from = 64, to = 65), AutoMigration(from = 65, to = 66), + AutoMigration(from = 66, to = 67), ] ) @TypeConverters( @@ -230,6 +235,7 @@ const val WC_DATABASE_VERSION = 66 RemoteIdConverter::class, BigDecimalConverter::class, CurrencyPositionConverter::class, + StatsGranularityConverter::class, ] ) abstract class WCAndroidDatabase : RoomDatabase(), TransactionExecutor { @@ -269,17 +275,20 @@ abstract class WCAndroidDatabase : RoomDatabase(), TransactionExecutor { internal abstract val globalAttributesDao: GlobalAttributesDao internal abstract val gatewaysDao: GatewaysDao internal abstract val newVisitorStatsDao: NewVisitorStatsDao + internal abstract val revenueStatsDao: RevenueStatsDao companion object { fun buildDb( applicationContext: Context, - currencyPositionConverter: CurrencyPositionConverter + currencyPositionConverter: CurrencyPositionConverter, + statsGranularityConverter: StatsGranularityConverter, ) = Room.databaseBuilder( applicationContext, WCAndroidDatabase::class.java, "wc-android-database" ).allowMainThreadQueries() .addTypeConverter(currencyPositionConverter) + .addTypeConverter(statsGranularityConverter) .fallbackToDestructiveMigrationOnDowngrade() .fallbackToDestructiveMigrationFrom(1, 2) .addMigrations(MIGRATION_3_4) diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/WCStatsSqlUtils.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/WCStatsSqlUtils.kt deleted file mode 100644 index baa4736d0875..000000000000 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/WCStatsSqlUtils.kt +++ /dev/null @@ -1,75 +0,0 @@ -package org.wordpress.android.fluxc.persistence - -import com.wellsql.generated.WCRevenueStatsModelTable -import com.yarolegovich.wellsql.WellSql -import org.wordpress.android.fluxc.model.SiteModel -import org.wordpress.android.fluxc.model.WCRevenueStatsModel -import org.wordpress.android.fluxc.store.WCStatsStore.StatsGranularity - -object WCStatsSqlUtils { - /** - * Methods to support the new v4 revenue stats api - */ - fun insertOrUpdateRevenueStats(stats: WCRevenueStatsModel): Int { - val statsResult = searchMatchingRevenueStatsInDatabase(stats) - - return if (statsResult.isEmpty()) { - // insert - WellSql.insert(stats).asSingleTransaction(true).execute() - 1 - } else { - // Update - val oldId = statsResult[0].id - WellSql.update(WCRevenueStatsModel::class.java).whereId(oldId) - .put(stats, UpdateAllExceptId(WCRevenueStatsModel::class.java)).execute() - } - } - - private fun searchMatchingRevenueStatsInDatabase(stats: WCRevenueStatsModel): List { - return if (stats.rangeId.isNotEmpty()) { - WellSql.select(WCRevenueStatsModel::class.java) - .where().beginGroup() - .equals(WCRevenueStatsModelTable.LOCAL_SITE_ID, stats.localSiteId) - .equals(WCRevenueStatsModelTable.RANGE_ID, stats.rangeId) - .endGroup().endWhere() - .asModel - } else { - WellSql.select(WCRevenueStatsModel::class.java) - .where().beginGroup() - .equals(WCRevenueStatsModelTable.LOCAL_SITE_ID, stats.localSiteId) - .equals(WCRevenueStatsModelTable.INTERVAL, stats.interval) - .equals(WCRevenueStatsModelTable.START_DATE, stats.startDate) - .equals(WCRevenueStatsModelTable.END_DATE, stats.endDate) - .endGroup().endWhere() - .asModel - } - } - - fun getRevenueStatsForSiteIntervalAndDate( - site: SiteModel, - granularity: StatsGranularity, - startDate: String, - endDate: String - ): WCRevenueStatsModel? { - return WellSql.select(WCRevenueStatsModel::class.java) - .where() - .beginGroup() - .equals(WCRevenueStatsModelTable.LOCAL_SITE_ID, site.id) - .equals(WCRevenueStatsModelTable.INTERVAL, granularity) - .equals(WCRevenueStatsModelTable.START_DATE, startDate) - .equals(WCRevenueStatsModelTable.END_DATE, endDate) - .endGroup().endWhere() - .asModel.firstOrNull() - } - - fun getRevenueStatsFromRangeId( - site: SiteModel, - revenueRangeId: String - ) = WellSql.select(WCRevenueStatsModel::class.java) - .where() - .beginGroup() - .equals(WCRevenueStatsModelTable.LOCAL_SITE_ID, site.id) - .equals(WCRevenueStatsModelTable.RANGE_ID, revenueRangeId) - .endGroup().endWhere() - .asModel.firstOrNull() -} diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/converters/StatsGranularityConverter.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/converters/StatsGranularityConverter.kt new file mode 100644 index 000000000000..ca920b160668 --- /dev/null +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/converters/StatsGranularityConverter.kt @@ -0,0 +1,28 @@ +package org.wordpress.android.fluxc.persistence.converters + +import androidx.room.ProvidedTypeConverter +import androidx.room.TypeConverter +import org.wordpress.android.fluxc.store.WCStatsStore.StatsGranularity +import org.wordpress.android.fluxc.utils.AppLogWrapper +import org.wordpress.android.util.AppLog +import javax.inject.Inject + +@ProvidedTypeConverter +class StatsGranularityConverter @Inject constructor( + private val logger: AppLogWrapper +) { + @TypeConverter + fun fromStatsGranularity(value: StatsGranularity): String { + return value.name + } + + @TypeConverter + fun toStatsGranularity(value: String): StatsGranularity { + return try { + StatsGranularity.valueOf(value.uppercase()) + } catch (e: IllegalArgumentException) { + logger.e(AppLog.T.DB, "Error converting $value", e) + StatsGranularity.HOURS + } + } +} diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/dao/RevenueStatsDao.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/dao/RevenueStatsDao.kt new file mode 100644 index 000000000000..1004059f9d2f --- /dev/null +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/dao/RevenueStatsDao.kt @@ -0,0 +1,43 @@ +package org.wordpress.android.fluxc.persistence.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId +import org.wordpress.android.fluxc.model.WCRevenueStatsModel +import org.wordpress.android.fluxc.store.WCStatsStore.StatsGranularity + +@Dao +internal abstract class RevenueStatsDao { + @Query( + """ + SELECT * FROM RevenueStatsEntity + WHERE localSiteId = :siteId + AND interval = :granularity + AND startDate = :startDate + AND endDate = :endDate + """ + ) + abstract suspend fun getBySiteIntervalAndDate( + siteId: LocalId, + granularity: StatsGranularity, + startDate: String, + endDate: String + ): WCRevenueStatsModel? + + @Query( + """ + SELECT * FROM RevenueStatsEntity + WHERE localSiteId = :siteId + AND rangeId = :rangeId + """ + ) + abstract suspend fun getBySiteAndRangeId( + siteId: LocalId, + rangeId: String + ): WCRevenueStatsModel? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract suspend fun insert(entity: WCRevenueStatsModel) +} diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/store/WCStatsStore.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/store/WCStatsStore.kt index 0f8d44f803e3..f6609c62573d 100644 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/store/WCStatsStore.kt +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/store/WCStatsStore.kt @@ -22,8 +22,8 @@ import org.wordpress.android.fluxc.network.rest.wpcom.wc.bundlestats.BundleStats import org.wordpress.android.fluxc.network.rest.wpcom.wc.orderstats.OrderStatsRestClient import org.wordpress.android.fluxc.network.rest.wpcom.wc.orderstats.OrderStatsRestClient.OrderStatsApiUnit import org.wordpress.android.fluxc.network.rest.wpcom.wc.orderstats.VisitorStatsSummaryApiResponse -import org.wordpress.android.fluxc.persistence.WCStatsSqlUtils import org.wordpress.android.fluxc.persistence.dao.NewVisitorStatsDao +import org.wordpress.android.fluxc.persistence.dao.RevenueStatsDao import org.wordpress.android.fluxc.persistence.dao.VisitorSummaryStatsDao import org.wordpress.android.fluxc.persistence.entity.VisitorSummaryStatsEntity import org.wordpress.android.fluxc.persistence.entity.toDomainModel @@ -42,6 +42,7 @@ class WCStatsStore @Inject internal constructor( private val coroutineEngine: CoroutineEngine, private val visitorSummaryStatsDao: VisitorSummaryStatsDao, private val newVisitorStatsDao: NewVisitorStatsDao, + private val revenueStatsDao: RevenueStatsDao, ) : Store(dispatcher) { companion object { private const val DATE_FORMAT_DAY = "yyyy-MM-dd" @@ -85,7 +86,7 @@ class WCStatsStore @Inject internal constructor( val startDate: String, val endDate: String, val forced: Boolean = false, - val revenueRangeId: String = "" + val revenueRangeId: String, ) : Payload() class FetchRevenueStatsResponsePayload( @@ -106,10 +107,9 @@ class WCStatsStore @Inject internal constructor( ) : Payload() class FetchRevenueStatsAvailabilityResponsePayload( - val site: SiteModel, - val available: Boolean = false + val site: SiteModel ) : Payload() { - constructor(error: OrderStatsError, site: SiteModel, available: Boolean) : this(site, available) { + constructor(error: OrderStatsError, site: SiteModel) : this(site) { this.error = error } } @@ -390,11 +390,9 @@ class WCStatsStore @Inject internal constructor( * Methods to support v4 revenue api changes */ class OnWCRevenueStatsChanged( - val rowsAffected: Int, val granularity: StatsGranularity, val startDate: String? = null, val endDate: String? = null, - val availability: Boolean = false ) : OnChanged() { var causeOfChange: WCStatsAction? = null } @@ -416,15 +414,14 @@ class WCStatsStore @Inject internal constructor( with(result) { return@withDefaultContext if (isError || stats == null) { - OnWCRevenueStatsChanged(0, granularity) + OnWCRevenueStatsChanged(granularity) .also { it.error = error } } else { - val rowsAffected = WCStatsSqlUtils.insertOrUpdateRevenueStats(stats) + revenueStatsDao.insert(stats) OnWCRevenueStatsChanged( - rowsAffected, granularity, stats.startDate, - stats.endDate + stats.endDate, ) } } @@ -492,11 +489,11 @@ class WCStatsStore @Inject internal constructor( val onStatsChanged = with(payload) { if (isError) { return@with OnWCRevenueStatsChanged( - 0, granularity = StatsGranularity.YEARS, availability = payload.available + granularity = StatsGranularity.YEARS ).also { it.error = payload.error } } else { return@with OnWCRevenueStatsChanged( - 0, granularity = StatsGranularity.YEARS, availability = payload.available + granularity = StatsGranularity.YEARS ) } } @@ -504,7 +501,7 @@ class WCStatsStore @Inject internal constructor( emitChange(onStatsChanged) } - fun getGrossRevenueStats( + suspend fun getGrossRevenueStats( site: SiteModel, granularity: StatsGranularity, startDate: String, @@ -516,7 +513,7 @@ class WCStatsStore @Inject internal constructor( } ?: mapOf() } - fun getOrderCountStats( + suspend fun getOrderCountStats( site: SiteModel, granularity: StatsGranularity, startDate: String, @@ -528,19 +525,15 @@ class WCStatsStore @Inject internal constructor( } ?: mapOf() } - fun getRawRevenueStats( + suspend fun getRawRevenueStats( site: SiteModel, granularity: StatsGranularity, startDate: String, endDate: String - ): WCRevenueStatsModel? { - return WCStatsSqlUtils.getRevenueStatsForSiteIntervalAndDate( - site, granularity, startDate, endDate - ) - } + ) = revenueStatsDao.getBySiteIntervalAndDate(site.localId(), granularity, startDate, endDate) - fun getRawRevenueStatsFromRangeId( + suspend fun getRawRevenueStatsFromRangeId( site: SiteModel, revenueRangeId: String - ) = WCStatsSqlUtils.getRevenueStatsFromRangeId(site, revenueRangeId) + ) = revenueStatsDao.getBySiteAndRangeId(site.localId(), revenueRangeId) } diff --git a/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/model/WCRevenueStatsModelTest.kt b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/model/WCRevenueStatsModelTest.kt index d51ccbdee509..ddcaff1c9116 100644 --- a/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/model/WCRevenueStatsModelTest.kt +++ b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/model/WCRevenueStatsModelTest.kt @@ -2,16 +2,22 @@ package org.wordpress.android.fluxc.model import org.assertj.core.api.Assertions.assertThat import org.junit.Test +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.utils.json +@Suppress("UnitTestNamingRule") class WCRevenueStatsModelTest { @Test fun `should assign null value if number of sold items exceeds integer range`() { - val sut = WCRevenueStatsModel().apply { - total = json { - "num_items_sold" To "15032797007" - }.toString() - } + val sut = WCRevenueStatsModel( + localSiteId = LocalId(1), + interval = "", + startDate = "", + endDate = "", + data = "", + total = json { "num_items_sold" To "15032797007" }.toString(), + rangeId = "", + ) val total = sut.parseTotal() @@ -20,11 +26,15 @@ class WCRevenueStatsModelTest { @Test fun `should assign null value if number of sold items is negative`() { - val sut = WCRevenueStatsModel().apply { - total = json { - "num_items_sold" To "-123" - }.toString() - } + val sut = WCRevenueStatsModel( + localSiteId = LocalId(1), + interval = "", + startDate = "", + endDate = "", + data = "", + total = json { "num_items_sold" To "-123" }.toString(), + rangeId = "", + ) val total = sut.parseTotal() @@ -33,11 +43,15 @@ class WCRevenueStatsModelTest { @Test fun `should correctly parse value if number of sold items is within integer limits`() { - val sut = WCRevenueStatsModel().apply { - total = json { - "num_items_sold" To "123456" - }.toString() - } + val sut = WCRevenueStatsModel( + localSiteId = LocalId(1), + interval = "", + startDate = "", + endDate = "", + data = "", + total = json { "num_items_sold" To "123456" }.toString(), + rangeId = "", + ) val total = sut.parseTotal() diff --git a/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/converters/CurrencyPositionConverterTest.kt b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/converters/CurrencyPositionConverterTest.kt index 77bb50f482b7..5b38727a98c4 100644 --- a/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/converters/CurrencyPositionConverterTest.kt +++ b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/converters/CurrencyPositionConverterTest.kt @@ -7,7 +7,7 @@ import org.wordpress.android.fluxc.model.settings.CurrencyPosition class CurrencyPositionConverterTest { @Test - fun `returns default value if the db-stored value is invalid`() { + fun `when the db-stored value is invalid, then it returns default value`() { val converter = CurrencyPositionConverter(logger = mock()) val invalidValue = "invalid_currency_position" diff --git a/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/converters/StatsGranularityConverterTest.kt b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/converters/StatsGranularityConverterTest.kt new file mode 100644 index 000000000000..87ecc6931c59 --- /dev/null +++ b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/converters/StatsGranularityConverterTest.kt @@ -0,0 +1,18 @@ +package org.wordpress.android.fluxc.persistence.converters + +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import org.mockito.Mockito.mock +import org.wordpress.android.fluxc.store.WCStatsStore + +class StatsGranularityConverterTest { + @Test + fun `when the db-stored value is invalid, then it returns default value`() { + val converter = StatsGranularityConverter(logger = mock()) + val invalidValue = "invalid_stats_granularity" + + val result = converter.toStatsGranularity(invalidValue) + + assertThat(result).isEqualTo(WCStatsStore.StatsGranularity.HOURS) + } +} diff --git a/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/dao/RevenueStatsDaoTest.kt b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/dao/RevenueStatsDaoTest.kt new file mode 100644 index 000000000000..a4b73dc64eae --- /dev/null +++ b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/persistence/dao/RevenueStatsDaoTest.kt @@ -0,0 +1,164 @@ +package org.wordpress.android.fluxc.persistence.dao + +import android.app.Application +import androidx.test.core.app.ApplicationProvider +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId +import org.wordpress.android.fluxc.model.WCRevenueStatsModel +import org.wordpress.android.fluxc.persistence.DatabaseTestRule +import org.wordpress.android.fluxc.store.WCStatsStore.StatsGranularity + +@Config(manifest = Config.NONE) +@RunWith(RobolectricTestRunner::class) +class RevenueStatsDaoTest { + private lateinit var dao: RevenueStatsDao + + @Rule + @JvmField + val databaseRule = DatabaseTestRule(ApplicationProvider.getApplicationContext()) + + @Before + fun setUp() { + dao = databaseRule.db.revenueStatsDao + } + + @Test + fun `when insert revenue stats, then get by site+interval+date returns entity`() = runTest { + val stat = revenue( + siteId1, + StatsGranularity.DAYS.toString(), + "2024-08-01T00:00:00", + "2024-08-01T23:59:59", + ) + dao.insert(stat) + + val retrieved = dao.getBySiteIntervalAndDate( + siteId = stat.localSiteId, + granularity = StatsGranularity.valueOf(stat.interval), + startDate = stat.startDate, + endDate = stat.endDate + ) + + assertThat(retrieved).isEqualTo(stat) + } + + @Test + fun `given multiple sites, when get by site+interval+date called, then returns specific site stats`() = runTest { + val site1Stat = revenue( + siteId1, + StatsGranularity.DAYS.toString(), + "2024-08-02T00:00:00", + "2024-08-02T23:59:59", + ) + dao.insert(site1Stat) + val site2Stat = revenue( + siteId2, + StatsGranularity.WEEKS.toString(), + "2024-08-02T00:00:00", + "2024-08-22T23:59:59", + ) + dao.insert(site2Stat) + + val retrievedSite1 = + dao.getBySiteIntervalAndDate( + site1Stat.localSiteId, + StatsGranularity.valueOf(site1Stat.interval), + site1Stat.startDate, + site1Stat.endDate + ) + val retrievedSite2 = + dao.getBySiteIntervalAndDate( + site2Stat.localSiteId, + StatsGranularity.valueOf(site2Stat.interval), + site2Stat.startDate, + site2Stat.endDate + ) + + assertThat(retrievedSite1).isEqualTo(site1Stat) + assertThat(retrievedSite2).isEqualTo(site2Stat) + } + + @Test + fun `when insert with rangeId, then get by site+rangeId returns entity`() = runTest { + val stat = revenue( + siteId1, + StatsGranularity.MONTHS.toString(), + "2024-08-01T00:00:00", + "2024-08-31T23:59:59", + rangeId = "RANGE-123" + ) + dao.insert(stat) + + val retrieved = dao.getBySiteAndRangeId( + stat.localSiteId, + "RANGE-123" + ) + + assertThat(retrieved).isEqualTo(stat) + } + + @Test + fun `when insert twice with same (siteId, rangeId), then row is replaced`() = runTest { + val initialStat = revenue( + siteId1, + StatsGranularity.MONTHS.toString(), + "2024-07-01T00:00:00", + "2024-07-31T23:59:59", + rangeId = "RID-1", + total = "{\"orders_count\":1}" + ) + val updatedStat = revenue( + siteId1, + StatsGranularity.MONTHS.toString(), + "2024-07-01T00:00:00", + "2024-07-31T23:59:59", + rangeId = "RID-1", + total = "{\"orders_count\":2}" + ) + + dao.insert(initialStat) + var retrieved = dao.getBySiteAndRangeId( + siteId1, + "RID-1" + ) + assertThat(retrieved).isEqualTo(initialStat) + + dao.insert(updatedStat) + retrieved = dao.getBySiteAndRangeId( + siteId1, + "RID-1" + ) + assertThat(retrieved).isEqualTo(updatedStat) + } + + @Suppress("LongParameterList") + private fun revenue( + siteId: LocalId, + interval: String, + start: String, + end: String, + data: String = "[]", + total: String = "{}", + rangeId: String = "", + ) = WCRevenueStatsModel( + localSiteId = siteId, + interval = interval, + startDate = start, + endDate = end, + data = data, + total = total, + rangeId = rangeId, + ) + + companion object { + private val siteId1 = LocalId(1) + private val siteId2 = LocalId(2) + } +} diff --git a/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/store/WCStatsStoreTest.kt b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/store/WCStatsStoreTest.kt index 8606ce5a114a..705596051550 100644 --- a/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/store/WCStatsStoreTest.kt +++ b/libs/fluxc-plugin/src/test/java/org/wordpress/android/fluxc/store/WCStatsStoreTest.kt @@ -2,7 +2,6 @@ package org.wordpress.android.fluxc.store import android.app.Application import androidx.test.core.app.ApplicationProvider -import com.yarolegovich.wellsql.WellSql import org.hamcrest.CoreMatchers.anyOf import org.hamcrest.CoreMatchers.not import org.hamcrest.MatcherAssert.assertThat @@ -19,8 +18,8 @@ import org.mockito.kotlin.whenever import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.wordpress.android.fluxc.Dispatcher -import org.wordpress.android.fluxc.SingleStoreWellSqlConfigForTests import org.wordpress.android.fluxc.UnitTestUtils +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.WCRevenueStatsModel import org.wordpress.android.fluxc.model.WCVisitorStatsSummary @@ -34,7 +33,6 @@ import org.wordpress.android.fluxc.network.rest.wpcom.wc.bundlestats.BundleStats import org.wordpress.android.fluxc.network.rest.wpcom.wc.orderstats.OrderStatsRestClient import org.wordpress.android.fluxc.network.rest.wpcom.wc.orderstats.VisitorStatsSummaryApiResponse import org.wordpress.android.fluxc.persistence.DatabaseTestRule -import org.wordpress.android.fluxc.persistence.WellSqlConfig import org.wordpress.android.fluxc.store.WCStatsStore.FetchRevenueStatsPayload import org.wordpress.android.fluxc.store.WCStatsStore.FetchRevenueStatsResponsePayload import org.wordpress.android.fluxc.store.WCStatsStore.StatsGranularity @@ -67,14 +65,6 @@ class WCStatsStoreTest { @Before fun setUp() { - val config = SingleStoreWellSqlConfigForTests( - context, - listOf(WCRevenueStatsModel::class.java), - WellSqlConfig.Companion.ADDON_WOOCOMMERCE - ) - WellSql.init(config) - config.reset() - wcStatsStore = WCStatsStore( dispatcher = Dispatcher(), wcOrderStatsClient = mockOrderStatsRestClient, @@ -82,6 +72,7 @@ class WCStatsStoreTest { coroutineEngine = initCoroutineEngine(), visitorSummaryStatsDao = databaseRule.db.visitorSummaryStatsDao, newVisitorStatsDao = databaseRule.db.newVisitorStatsDao, + revenueStatsDao = databaseRule.db.revenueStatsDao, ) } @@ -385,11 +376,14 @@ class WCStatsStoreTest { any(), any() ) - ).thenReturn(FetchRevenueStatsResponsePayload(it, StatsGranularity.DAYS, WCRevenueStatsModel())) - val startDate = DateUtils.getStartDateForSite(it, DateUtils.formatDate("yyyy-MM-dd'T'00:00:00", Date())) - val endDate = DateUtils.getEndDateForSite(it) - val payload = FetchRevenueStatsPayload(it, StatsGranularity.DAYS, startDate, endDate) - wcStatsStore.fetchRevenueStats(payload) + ).thenReturn( + FetchRevenueStatsResponsePayload( + it, + StatsGranularity.DAYS, + WCRevenueStatsModel.fake() + ) + ) + wcStatsStore.fetchRevenueStats(revenuePayload(it)) val timeOnSite = getCurrentDateTimeForSite(it, "yyyy-MM-dd'T'00:00:00") @@ -424,11 +418,14 @@ class WCStatsStoreTest { any(), any() ) - ).thenReturn(FetchRevenueStatsResponsePayload(it, StatsGranularity.DAYS, WCRevenueStatsModel())) - val startDate = DateUtils.getStartDateForSite(it, DateUtils.formatDate("yyyy-MM-dd'T'00:00:00", Date())) - val endDate = DateUtils.getEndDateForSite(it) - val payload = FetchRevenueStatsPayload(it, StatsGranularity.DAYS, startDate, endDate) - wcStatsStore.fetchRevenueStats(payload) + ).thenReturn( + FetchRevenueStatsResponsePayload( + it, + StatsGranularity.DAYS, + WCRevenueStatsModel.fake() + ) + ) + wcStatsStore.fetchRevenueStats(revenuePayload(it)) val timeOnSite = getCurrentDateTimeForSite(it, "yyyy-MM-dd'T'00:00:00") @@ -471,12 +468,13 @@ class WCStatsStoreTest { any(), any() ) - ).thenReturn(FetchRevenueStatsResponsePayload(it, StatsGranularity.DAYS, WCRevenueStatsModel())) - val startDate = DateUtils.getStartDateForSite(it, DateUtils.formatDate("yyyy-MM-dd'T'00:00:00", Date())) - val endDate = DateUtils.formatDate("yyyy-MM-dd", Date()) - - val payload = FetchRevenueStatsPayload(it, StatsGranularity.DAYS, startDate, endDate) - wcStatsStore.fetchRevenueStats(payload) + ).thenReturn( + FetchRevenueStatsResponsePayload( + it, StatsGranularity.DAYS, + WCRevenueStatsModel.fake() + ) + ) + wcStatsStore.fetchRevenueStats(revenuePayload(it)) val timeOnSite = getCurrentDateTimeForSite(it, "yyyy-MM-dd'T'00:00:00") @@ -511,11 +509,13 @@ class WCStatsStoreTest { any(), any() ) - ).thenReturn(FetchRevenueStatsResponsePayload(it, StatsGranularity.DAYS, WCRevenueStatsModel())) - val startDate = DateUtils.getStartDateForSite(it, DateUtils.formatDate("yyyy-MM-dd'T'00:00:00", Date())) - val endDate = DateUtils.getEndDateForSite(it) - val payload = FetchRevenueStatsPayload(it, StatsGranularity.DAYS, startDate, endDate) - wcStatsStore.fetchRevenueStats(payload) + ).thenReturn( + FetchRevenueStatsResponsePayload( + it, StatsGranularity.DAYS, + WCRevenueStatsModel.fake() + ) + ) + wcStatsStore.fetchRevenueStats(revenuePayload(it)) val timeOnSite = getCurrentDateTimeForSite(it, "yyyy-MM-dd'T'00:00:00") @@ -548,7 +548,7 @@ class WCStatsStoreTest { fun testGetRevenueAndOrderStatsForSite() = test { // revenue stats model for current day val currentDayStatsModel = WCStatsTestUtils.generateSampleRevenueStatsModel() - val site = SiteModel().apply { id = currentDayStatsModel.localSiteId } + val site1 = SiteModel().apply { id = currentDayStatsModel.localSiteId.value } val currentDayGranularity = StatsGranularity.valueOf( currentDayStatsModel.interval.uppercase( Locale.getDefault() @@ -567,27 +567,28 @@ class WCStatsStoreTest { ) ).thenReturn( FetchRevenueStatsResponsePayload( - site, + site1, currentDayGranularity, currentDayStatsModel ) ) wcStatsStore.fetchRevenueStats( FetchRevenueStatsPayload( - site, - currentDayGranularity, - currentDayStatsModel.startDate, - currentDayStatsModel.endDate + site = site1, + granularity = currentDayGranularity, + startDate = currentDayStatsModel.startDate, + endDate = currentDayStatsModel.endDate, + revenueRangeId = "${currentDayStatsModel.startDate}${currentDayStatsModel.endDate}", ) ) // verify that the revenue stats & order count is not empty val currentDayRevenueStats = wcStatsStore.getGrossRevenueStats( - site, StatsGranularity.valueOf(currentDayStatsModel.interval.uppercase(Locale.getDefault())), + site1, StatsGranularity.valueOf(currentDayStatsModel.interval.uppercase(Locale.getDefault())), currentDayStatsModel.startDate, currentDayStatsModel.endDate ) val currentDayOrderStats = wcStatsStore.getOrderCountStats( - site, StatsGranularity.valueOf(currentDayStatsModel.interval.uppercase(Locale.getDefault())), + site1, StatsGranularity.valueOf(currentDayStatsModel.interval.uppercase(Locale.getDefault())), currentDayStatsModel.startDate, currentDayStatsModel.endDate ) @@ -605,7 +606,7 @@ class WCStatsStoreTest { ) ) val currentWeekPayload = FetchRevenueStatsResponsePayload( - site, currentWeekGranularity, currentWeekStatsModel + site1, currentWeekGranularity, currentWeekStatsModel ) whenever( mockOrderStatsRestClient.fetchRevenueStats( @@ -621,20 +622,21 @@ class WCStatsStoreTest { ).thenReturn(currentWeekPayload) wcStatsStore.fetchRevenueStats( FetchRevenueStatsPayload( - site, - currentWeekGranularity, - currentWeekStatsModel.startDate, - currentWeekStatsModel.endDate + site = site1, + granularity = currentWeekGranularity, + startDate = currentWeekStatsModel.startDate, + endDate = currentWeekStatsModel.endDate, + revenueRangeId = "${currentDayStatsModel.startDate}${currentDayStatsModel.endDate}", ) ) // verify that the revenue stats & order count is not empty val currentWeekRevenueStats = wcStatsStore.getGrossRevenueStats( - site, StatsGranularity.valueOf(currentWeekStatsModel.interval.uppercase(Locale.getDefault())), + site1, StatsGranularity.valueOf(currentWeekStatsModel.interval.uppercase(Locale.getDefault())), currentWeekStatsModel.startDate, currentWeekStatsModel.endDate ) val currentWeekOrderStats = wcStatsStore.getOrderCountStats( - site, StatsGranularity.valueOf(currentWeekStatsModel.interval.uppercase(Locale.getDefault())), + site1, StatsGranularity.valueOf(currentWeekStatsModel.interval.uppercase(Locale.getDefault())), currentWeekStatsModel.startDate, currentWeekStatsModel.endDate ) @@ -652,7 +654,7 @@ class WCStatsStoreTest { ) ) val currentMonthPayload = FetchRevenueStatsResponsePayload( - site, currentMonthGranularity, currentMonthStatsModel + site1, currentMonthGranularity, currentMonthStatsModel ) whenever( mockOrderStatsRestClient.fetchRevenueStats( @@ -668,20 +670,21 @@ class WCStatsStoreTest { ).thenReturn(currentMonthPayload) wcStatsStore.fetchRevenueStats( FetchRevenueStatsPayload( - site, - currentMonthGranularity, - currentMonthStatsModel.startDate, - currentMonthStatsModel.endDate + site = site1, + granularity = currentMonthGranularity, + startDate = currentMonthStatsModel.startDate, + endDate = currentMonthStatsModel.endDate, + revenueRangeId = "${currentDayStatsModel.startDate}${currentDayStatsModel.endDate}", ) ) // verify that the revenue stats & order count is not empty val currentMonthRevenueStats = wcStatsStore.getGrossRevenueStats( - site, StatsGranularity.valueOf(currentMonthStatsModel.interval.uppercase(Locale.getDefault())), + site1, StatsGranularity.valueOf(currentMonthStatsModel.interval.uppercase(Locale.getDefault())), currentMonthStatsModel.startDate, currentMonthStatsModel.endDate ) val currentMonthOrderStats = wcStatsStore.getOrderCountStats( - site, StatsGranularity.valueOf(currentMonthStatsModel.interval.uppercase(Locale.getDefault())), + site1, StatsGranularity.valueOf(currentMonthStatsModel.interval.uppercase(Locale.getDefault())), currentMonthStatsModel.startDate, currentMonthStatsModel.endDate ) @@ -699,7 +702,7 @@ class WCStatsStoreTest { ) ) val allSiteCurrentDayPayload = FetchRevenueStatsResponsePayload( - site, allSiteCurrentDayGranularity, altSiteOrderStatsModel + site2, allSiteCurrentDayGranularity, altSiteOrderStatsModel ) whenever( mockOrderStatsRestClient.fetchRevenueStats( @@ -715,20 +718,21 @@ class WCStatsStoreTest { ).thenReturn(allSiteCurrentDayPayload) wcStatsStore.fetchRevenueStats( FetchRevenueStatsPayload( - site, - allSiteCurrentDayGranularity, - altSiteOrderStatsModel.startDate, - altSiteOrderStatsModel.endDate + site = site2, + granularity = allSiteCurrentDayGranularity, + startDate = altSiteOrderStatsModel.startDate, + endDate = altSiteOrderStatsModel.endDate, + revenueRangeId = "${currentDayStatsModel.startDate}${currentDayStatsModel.endDate}", ) ) // verify that the revenue stats & order count is not empty val altSiteCurrentDayRevenueStats = wcStatsStore.getGrossRevenueStats( - site, StatsGranularity.valueOf(altSiteOrderStatsModel.interval.uppercase(Locale.getDefault())), + site2, StatsGranularity.valueOf(altSiteOrderStatsModel.interval.uppercase(Locale.getDefault())), altSiteOrderStatsModel.startDate, altSiteOrderStatsModel.endDate ) val altSiteCurrentDayOrderStats = wcStatsStore.getOrderCountStats( - site, StatsGranularity.valueOf(altSiteOrderStatsModel.interval.uppercase(Locale.getDefault())), + site2, StatsGranularity.valueOf(altSiteOrderStatsModel.interval.uppercase(Locale.getDefault())), altSiteOrderStatsModel.startDate, altSiteOrderStatsModel.endDate ) @@ -755,10 +759,11 @@ class WCStatsStoreTest { ).thenReturn(nonExistentPayload) wcStatsStore.fetchRevenueStats( FetchRevenueStatsPayload( - site, - nonExistentSiteGranularity, - altSiteOrderStatsModel.startDate, - altSiteOrderStatsModel.endDate + site = site2, + granularity = nonExistentSiteGranularity, + startDate = altSiteOrderStatsModel.startDate, + endDate = altSiteOrderStatsModel.endDate, + revenueRangeId = "${currentDayStatsModel.startDate}${currentDayStatsModel.endDate}", ) ) @@ -794,19 +799,20 @@ class WCStatsStoreTest { ).thenReturn(nonExistentPayload) wcStatsStore.fetchRevenueStats( FetchRevenueStatsPayload( - site = site, + site = site2, granularity = StatsGranularity.YEARS, startDate = "2019-01-01", - endDate = "2019-01-07" + endDate = "2019-01-07", + revenueRangeId = "${currentDayStatsModel.startDate}${currentDayStatsModel.endDate}", ) ) // verify that the revenue stats & order count is empty val missingRevenueStats = wcStatsStore.getGrossRevenueStats( - site, StatsGranularity.YEARS, "2019-01-01", "2019-01-07" + site2, StatsGranularity.YEARS, "2019-01-01", "2019-01-07" ) val missingOrderStats = wcStatsStore.getOrderCountStats( - site, StatsGranularity.YEARS, "2019-01-01", "2019-01-07" + site2, StatsGranularity.YEARS, "2019-01-01", "2019-01-07" ) assertTrue(missingRevenueStats.isEmpty()) assertTrue(missingOrderStats.isEmpty()) @@ -1183,4 +1189,32 @@ class WCStatsStoreTest { assertEquals(true, result.isError) } + + @Suppress("LongParameterList") + private fun revenuePayload( + site: SiteModel, + ): FetchRevenueStatsPayload { + val startDate = DateUtils.getStartDateForSite( + site, + DateUtils.formatDate("yyyy-MM-dd'T'00:00:00", Date()) + ) + val endDate = DateUtils.getEndDateForSite(site) + return FetchRevenueStatsPayload( + site = site, + granularity = StatsGranularity.DAYS, + startDate = startDate, + endDate = endDate, + revenueRangeId = "$startDate$endDate", + ) + } + + private fun WCRevenueStatsModel.Companion.fake() = WCRevenueStatsModel( + LocalId(1), + "", + "", + "", + "", + "", + "" + ) } diff --git a/libs/fluxc-plugin/src/testFixtures/kotlin/org/wordpress/android/fluxc/persistence/DatabaseTestRule.kt b/libs/fluxc-plugin/src/testFixtures/kotlin/org/wordpress/android/fluxc/persistence/DatabaseTestRule.kt index df5c7fa9d4cd..eeb5f696a571 100644 --- a/libs/fluxc-plugin/src/testFixtures/kotlin/org/wordpress/android/fluxc/persistence/DatabaseTestRule.kt +++ b/libs/fluxc-plugin/src/testFixtures/kotlin/org/wordpress/android/fluxc/persistence/DatabaseTestRule.kt @@ -6,6 +6,7 @@ import org.junit.rules.TestWatcher import org.junit.runner.Description import org.mockito.Mockito import org.wordpress.android.fluxc.persistence.converters.CurrencyPositionConverter +import org.wordpress.android.fluxc.persistence.converters.StatsGranularityConverter class DatabaseTestRule(private val appContext: Context) : TestWatcher() { @@ -14,6 +15,7 @@ class DatabaseTestRule(private val appContext: Context) : TestWatcher() { override fun starting(description: Description?) { db = Room.inMemoryDatabaseBuilder(appContext, WCAndroidDatabase::class.java) .addTypeConverter(CurrencyPositionConverter(Mockito.mock())) + .addTypeConverter(StatsGranularityConverter(Mockito.mock())) .allowMainThreadQueries() .build() } diff --git a/libs/fluxc-plugin/src/testFixtures/kotlin/org/wordpress/android/fluxc/wc/stats/WCStatsTestUtils.kt b/libs/fluxc-plugin/src/testFixtures/kotlin/org/wordpress/android/fluxc/wc/stats/WCStatsTestUtils.kt index e72dde309e88..e29ab87ddde0 100644 --- a/libs/fluxc-plugin/src/testFixtures/kotlin/org/wordpress/android/fluxc/wc/stats/WCStatsTestUtils.kt +++ b/libs/fluxc-plugin/src/testFixtures/kotlin/org/wordpress/android/fluxc/wc/stats/WCStatsTestUtils.kt @@ -1,7 +1,7 @@ package org.wordpress.android.fluxc.wc.stats import org.wordpress.android.fluxc.UnitTestUtils -import org.wordpress.android.fluxc.model.LocalOrRemoteId +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.WCNewVisitorStatsModel import org.wordpress.android.fluxc.model.WCRevenueStatsModel import org.wordpress.android.fluxc.store.WCStatsStore.StatsGranularity @@ -25,13 +25,15 @@ object WCStatsTestUtils { endDate: String = dateTimeFormatter.format(LocalDate.now().atTime(23, 59, 59)), data: String = UnitTestUtils.getStringFromResourceFile(this.javaClass, "wc/revenue-stats-data.json") ): WCRevenueStatsModel { - return WCRevenueStatsModel().apply { - this.localSiteId = localSiteId - this.interval = interval - this.endDate = endDate - this.data = data - this.startDate = startDate - } + return WCRevenueStatsModel( + localSiteId = LocalId(localSiteId), + interval = interval, + endDate = endDate, + data = data, + startDate = startDate, + total = "", + rangeId = "", + ) } /** @@ -48,7 +50,7 @@ object WCStatsTestUtils { data: String = UnitTestUtils.getStringFromResourceFile(this.javaClass, "wc/visitor-stats-data.json") ): WCNewVisitorStatsModel { return WCNewVisitorStatsModel( - localSiteId = LocalOrRemoteId.LocalId(localSiteId), + localSiteId = LocalId(localSiteId), granularity = granularity, quantity = quantity, endDate = endDate, diff --git a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/wc/stats/WCStatsSqlUtilsTest.kt b/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/wc/stats/WCStatsSqlUtilsTest.kt deleted file mode 100644 index 07ad9062db5a..000000000000 --- a/libs/fluxc-tests/src/test/java/org/wordpress/android/fluxc/wc/stats/WCStatsSqlUtilsTest.kt +++ /dev/null @@ -1,209 +0,0 @@ -package org.wordpress.android.fluxc.wc.stats - -import com.yarolegovich.wellsql.WellSql -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.RuntimeEnvironment -import org.robolectric.annotation.Config -import org.wordpress.android.fluxc.SingleStoreWellSqlConfigForTests -import org.wordpress.android.fluxc.model.SiteModel -import org.wordpress.android.fluxc.model.WCRevenueStatsModel -import org.wordpress.android.fluxc.persistence.WCStatsSqlUtils -import org.wordpress.android.fluxc.persistence.WellSqlConfig -import org.wordpress.android.fluxc.store.WCStatsStore.StatsGranularity -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull - -@Config(manifest = Config.NONE) -@RunWith(RobolectricTestRunner::class) -class WCStatsSqlUtilsTest { - @Before - fun setUp() { - val appContext = RuntimeEnvironment.application.applicationContext - val config = SingleStoreWellSqlConfigForTests( - appContext, - listOf(WCRevenueStatsModel::class.java), - WellSqlConfig.ADDON_WOOCOMMERCE) - WellSql.init(config) - config.reset() - } - - @Test - @Suppress("LongMethod") - fun testSimpleInsertionAndRetrievalOfRevenueStats() { - // insert a first stats entry and verify that it is stored correctly - val revenueStatsModel = WCStatsTestUtils.generateSampleRevenueStatsModel() - WCStatsSqlUtils.insertOrUpdateRevenueStats(revenueStatsModel) - - with(WellSql.select(WCRevenueStatsModel::class.java).asModel) { - assertEquals(1, size) - assertEquals(revenueStatsModel.interval, first().interval) - assertEquals(revenueStatsModel.startDate, first().startDate) - assertEquals(revenueStatsModel.endDate, first().endDate) - } - - // Create a second stats entry for this site with same start & end date but with different interval - val revenueStatsModel2 = - WCStatsTestUtils.generateSampleRevenueStatsModel( - interval = StatsGranularity.MONTHS.toString(), data = "fake-data" - ) - WCStatsSqlUtils.insertOrUpdateRevenueStats(revenueStatsModel2) - - with(WellSql.select(WCRevenueStatsModel::class.java).asModel) { - assertEquals(2, size) - assertEquals(revenueStatsModel.interval, first().interval) - assertEquals(revenueStatsModel.startDate, first().startDate) - assertEquals(revenueStatsModel.endDate, first().endDate) - assertEquals(revenueStatsModel2.interval, get(1).interval) - assertEquals(revenueStatsModel2.startDate, get(1).startDate) - assertEquals(revenueStatsModel2.endDate, get(1).endDate) - } - - // Create a third stats entry for this site with same interval but different start & end date - val revenueStatsModel3 = - WCStatsTestUtils.generateSampleRevenueStatsModel( - data = "fake-data2", startDate = "2019-07-07 00:00:00", endDate = "2019-07-13 23:59:59" - ) - WCStatsSqlUtils.insertOrUpdateRevenueStats(revenueStatsModel3) - - with(WellSql.select(WCRevenueStatsModel::class.java).asModel) { - assertEquals(3, size) - assertEquals(revenueStatsModel.interval, first().interval) - assertEquals(revenueStatsModel.startDate, first().startDate) - assertEquals(revenueStatsModel.endDate, first().endDate) - assertEquals(revenueStatsModel2.interval, get(1).interval) - assertEquals(revenueStatsModel2.startDate, get(1).startDate) - assertEquals(revenueStatsModel2.endDate, get(1).endDate) - assertEquals(revenueStatsModel3.interval, get(2).interval) - assertEquals(revenueStatsModel3.startDate, get(2).startDate) - assertEquals(revenueStatsModel3.endDate, get(2).endDate) - } - - // Overwrite an existing entry and verify that update is happening correctly - val revenueStatsModel4 = WCStatsTestUtils.generateSampleRevenueStatsModel() - WCStatsSqlUtils.insertOrUpdateRevenueStats(revenueStatsModel4) - - with(WellSql.select(WCRevenueStatsModel::class.java).asModel) { - assertEquals(3, size) - assertEquals(revenueStatsModel.interval, first().interval) - assertEquals(revenueStatsModel.startDate, first().startDate) - assertEquals(revenueStatsModel.endDate, first().endDate) - assertEquals(revenueStatsModel2.interval, get(1).interval) - assertEquals(revenueStatsModel2.startDate, get(1).startDate) - assertEquals(revenueStatsModel2.endDate, get(1).endDate) - assertEquals(revenueStatsModel3.interval, get(2).interval) - assertEquals(revenueStatsModel3.startDate, get(2).startDate) - assertEquals(revenueStatsModel3.endDate, get(2).endDate) - } - - // Add another "day" entry, but for another site - val revenueStatsModel5 = WCStatsTestUtils.generateSampleRevenueStatsModel(localSiteId = 8) - WCStatsSqlUtils.insertOrUpdateRevenueStats(revenueStatsModel5) - - with(WellSql.select(WCRevenueStatsModel::class.java).asModel) { - assertEquals(4, size) - assertEquals(revenueStatsModel.interval, first().interval) - assertEquals(revenueStatsModel.startDate, first().startDate) - assertEquals(revenueStatsModel.endDate, first().endDate) - assertEquals(revenueStatsModel2.interval, get(1).interval) - assertEquals(revenueStatsModel2.startDate, get(1).startDate) - assertEquals(revenueStatsModel2.endDate, get(1).endDate) - assertEquals(revenueStatsModel3.interval, get(2).interval) - assertEquals(revenueStatsModel3.startDate, get(2).startDate) - assertEquals(revenueStatsModel3.endDate, get(2).endDate) - assertEquals(revenueStatsModel5.interval, get(3).interval) - assertEquals(revenueStatsModel5.localSiteId, get(3).localSiteId) - assertEquals(revenueStatsModel5.startDate, get(3).startDate) - assertEquals(revenueStatsModel5.endDate, get(3).endDate) - } - } - - @Test - @Suppress("LongMethod") - fun testGetRawRevenueStatsForSiteAndUnit() { - // revenue stats model for current day - val currentDayStatsModel = WCStatsTestUtils.generateSampleRevenueStatsModel() - val site = SiteModel().apply { id = currentDayStatsModel.localSiteId } - WCStatsSqlUtils.insertOrUpdateRevenueStats(currentDayStatsModel) - - // revenue stats model for this week - val currentWeekStatsModel = - WCStatsTestUtils.generateSampleRevenueStatsModel( - interval = StatsGranularity.WEEKS.toString(), - data = "fake-data", startDate = "2019-07-07", endDate = "2019-07-09" - ) - WCStatsSqlUtils.insertOrUpdateRevenueStats(currentWeekStatsModel) - - // revenue stats model for this month - val currentMonthStatsModel = - WCStatsTestUtils.generateSampleRevenueStatsModel( - interval = StatsGranularity.MONTHS.toString(), - data = "fake-data", startDate = "2019-07-01", endDate = "2019-07-09" - ) - WCStatsSqlUtils.insertOrUpdateRevenueStats(currentMonthStatsModel) - - // current day stats for alternate site - val site2 = SiteModel().apply { id = 8 } - val altSiteOrderStatsModel = WCStatsTestUtils.generateSampleRevenueStatsModel( - localSiteId = site2.id - ) - WCStatsSqlUtils.insertOrUpdateRevenueStats(altSiteOrderStatsModel) - - val currentDayStats = WCStatsSqlUtils.getRevenueStatsForSiteIntervalAndDate( - site, StatsGranularity.DAYS, currentDayStatsModel.startDate, currentDayStatsModel.endDate - ) - assertNotNull(currentDayStats) - with(currentDayStats) { - assertEquals(currentDayStatsModel.interval, interval) - assertEquals(currentDayStatsModel.startDate, startDate) - assertEquals(currentDayStatsModel.endDate, endDate) - assertEquals(currentDayStatsModel.localSiteId, localSiteId) - } - - val currentWeekStats = WCStatsSqlUtils.getRevenueStatsForSiteIntervalAndDate( - site, StatsGranularity.WEEKS, currentWeekStatsModel.startDate, currentWeekStatsModel.endDate - ) - assertNotNull(currentWeekStats) - with(currentWeekStats) { - assertEquals(currentWeekStatsModel.interval, interval) - assertEquals(currentWeekStatsModel.startDate, startDate) - assertEquals(currentWeekStatsModel.endDate, endDate) - assertEquals(currentWeekStatsModel.localSiteId, localSiteId) - } - - val currentMonthStats = WCStatsSqlUtils.getRevenueStatsForSiteIntervalAndDate( - site, StatsGranularity.MONTHS, currentMonthStatsModel.startDate, currentMonthStatsModel.endDate - ) - assertNotNull(currentMonthStats) - with(currentMonthStats) { - assertEquals(currentMonthStatsModel.interval, interval) - assertEquals(currentMonthStatsModel.startDate, startDate) - assertEquals(currentMonthStatsModel.endDate, endDate) - assertEquals(currentMonthStatsModel.localSiteId, localSiteId) - } - - val altCurrentDayStats = WCStatsSqlUtils.getRevenueStatsForSiteIntervalAndDate( - site2, StatsGranularity.DAYS, altSiteOrderStatsModel.startDate, altSiteOrderStatsModel.endDate - ) - assertNotNull(altCurrentDayStats) - with(altCurrentDayStats) { - assertEquals(altCurrentDayStats.interval, interval) - assertEquals(altSiteOrderStatsModel.startDate, startDate) - assertEquals(altSiteOrderStatsModel.endDate, endDate) - assertEquals(altSiteOrderStatsModel.localSiteId, localSiteId) - } - - val nonExistentSite = WCStatsSqlUtils.getRevenueStatsForSiteIntervalAndDate( - SiteModel().apply { id = 88 }, - StatsGranularity.DAYS, currentDayStatsModel.startDate, currentDayStatsModel.endDate - ) - assertNull(nonExistentSite) - - val missingData = WCStatsSqlUtils.getRevenueStatsForSiteIntervalAndDate( - site, StatsGranularity.YEARS, currentDayStatsModel.startDate, currentDayStatsModel.endDate) - assertNull(missingData) - } -} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt index 795b70800bdf..36fd44629ddc 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt @@ -40,7 +40,7 @@ open class WellSqlConfig : DefaultWellConfig { annotation class AddOn override fun getDbVersion(): Int { - return 231 + return 232 } override fun getDbName(): String { @@ -2252,6 +2252,10 @@ open class WellSqlConfig : DefaultWellConfig { db.execSQL("ALTER TABLE SiteModel ADD GARDEN_NAME TEXT") db.execSQL("ALTER TABLE SiteModel ADD GARDEN_PARTNER TEXT") } + + 231 -> migrateAddOn(ADDON_WOOCOMMERCE, version) { + db.execSQL("DROP TABLE IF EXISTS WCRevenueStatsModel") + } } } db.setTransactionSuccessful()