@@ -8,10 +8,12 @@ import Experiments
88import WooFoundation
99import SwiftUI
1010import enum Networking. DotcomError
11+ import protocol Storage. StorageManagerType
1112
1213final class OrderDetailsViewModel {
1314
1415 private let stores : StoresManager
16+ private let storageManager : StorageManagerType
1517 private let currencyFormatter : CurrencyFormatter
1618
1719 private( set) var order : Order
@@ -26,9 +28,11 @@ final class OrderDetailsViewModel {
2628
2729 init ( order: Order ,
2830 stores: StoresManager = ServiceLocator . stores,
31+ storageManager: StorageManagerType = ServiceLocator . storageManager,
2932 currencyFormatter: CurrencyFormatter = CurrencyFormatter ( currencySettings: ServiceLocator . currencySettings) ) {
3033 self . order = order
3134 self . stores = stores
35+ self . storageManager = storageManager
3236 self . currencyFormatter = currencyFormatter
3337 }
3438
@@ -265,12 +269,18 @@ extension OrderDetailsViewModel {
265269 }
266270
267271 func syncTrackingsEnablingAddButtonIfReachable( onReloadSections: ( ( ) -> ( ) ) ? = nil , onCompletion: ( ( ) -> Void ) ? = nil ) {
268- syncTracking { [ weak self] error in
269- if error == nil {
270- self ? . trackingIsReachable = true
272+ // If the plugin is not active, there is no point on continuing with a request that will fail.
273+ isPluginActive ( SitePlugin . SupportedPlugin. WCTracking) { [ weak self] isActive in
274+ guard let self = self , isActive else {
275+ onCompletion ? ( )
276+ return
277+ }
278+
279+ self . trackingIsReachable = true
280+ self . syncTracking { error in
281+ onReloadSections ? ( )
282+ onCompletion ? ( )
271283 }
272- onReloadSections ? ( )
273- onCompletion ? ( )
274284 }
275285 }
276286}
@@ -533,22 +543,31 @@ extension OrderDetailsViewModel {
533543 }
534544
535545 func syncShippingLabels( onCompletion: ( ( Error ? ) -> ( ) ) ? = nil ) {
536- let action = ShippingLabelAction . synchronizeShippingLabels ( siteID: order. siteID, orderID: order. orderID) { result in
537- switch result {
538- case . success:
539- ServiceLocator . analytics. track ( event: . shippingLabelsAPIRequest( result: . success) )
546+ // If the plugin is not active, there is no point on continuing with a request that will fail.
547+ isPluginActive ( SitePlugin . SupportedPlugin. WCShip) { [ weak self] isActive in
548+ guard let self = self , isActive else {
540549 onCompletion ? ( nil )
541- case . failure( let error) :
542- ServiceLocator . analytics. track ( event: . shippingLabelsAPIRequest( result: . failed( error: error) ) )
543- if error as? DotcomError == . noRestRoute {
544- DDLogError ( " ⚠️ Endpoint for synchronizing shipping labels is unreachable. WC Shipping plugin may be missing. " )
545- } else {
546- DDLogError ( " ⛔️ Error synchronizing shipping labels: \( error) " )
550+ return
551+ }
552+
553+ let action = ShippingLabelAction . synchronizeShippingLabels ( siteID: self . order. siteID, orderID: self . order. orderID) { result in
554+ switch result {
555+ case . success:
556+ ServiceLocator . analytics. track ( event: . shippingLabelsAPIRequest( result: . success) )
557+ onCompletion ? ( nil )
558+ case . failure( let error) :
559+ ServiceLocator . analytics. track ( event: . shippingLabelsAPIRequest( result: . failed( error: error) ) )
560+ if error as? DotcomError == . noRestRoute {
561+ DDLogError ( " ⚠️ Endpoint for synchronizing shipping labels is unreachable. WC Shipping plugin may be missing. " )
562+ } else {
563+ DDLogError ( " ⛔️ Error synchronizing shipping labels: \( error) " )
564+ }
565+ onCompletion ? ( error)
547566 }
548- onCompletion ? ( error)
549567 }
568+ self . stores. dispatch ( action)
569+
550570 }
551- stores. dispatch ( action)
552571 }
553572
554573 func syncSavedReceipts( onCompletion: ( ( Error ? ) -> ( ) ) ? = nil ) {
@@ -566,16 +585,24 @@ extension OrderDetailsViewModel {
566585 }
567586
568587 func checkShippingLabelCreationEligibility( onCompletion: ( ( ) -> Void ) ? = nil ) {
569- let action = ShippingLabelAction . checkCreationEligibility ( siteID: order. siteID,
570- orderID: order. orderID) { [ weak self] isEligible in
571- self ? . dataSource. isEligibleForShippingLabelCreation = isEligible
572- if isEligible, let orderStatus = self ? . orderStatus? . status. rawValue {
573- ServiceLocator . analytics. track ( . shippingLabelOrderIsEligible,
574- withProperties: [ " order_status " : orderStatus] )
588+ // If the plugin is not active, there is no point on continuing with a request that will fail.
589+ isPluginActive ( SitePlugin . SupportedPlugin. WCShip) { [ weak self] isActive in
590+ guard let self = self , isActive else {
591+ onCompletion ? ( )
592+ return
575593 }
576- onCompletion ? ( )
594+
595+ let action = ShippingLabelAction . checkCreationEligibility ( siteID: self . order. siteID,
596+ orderID: self . order. orderID) { [ weak self] isEligible in
597+ self ? . dataSource. isEligibleForShippingLabelCreation = isEligible
598+ if isEligible, let orderStatus = self ? . orderStatus? . status. rawValue {
599+ ServiceLocator . analytics. track ( . shippingLabelOrderIsEligible,
600+ withProperties: [ " order_status " : orderStatus] )
601+ }
602+ onCompletion ? ( )
603+ }
604+ self . stores. dispatch ( action)
577605 }
578- stores. dispatch ( action)
579606 }
580607
581608 func checkOrderAddOnFeatureSwitchState( onCompletion: ( ( ) -> Void ) ? = nil ) {
@@ -618,6 +645,32 @@ extension OrderDetailsViewModel {
618645
619646 stores. dispatch ( deleteTrackingAction)
620647 }
648+
649+ /// Helper function that returns `true` in its callback if the provided plugin name is active on the order's store.
650+ /// Additionally it logs to tracks if the plugin store is accessed without it being in sync so we can handle that edge-case if it happens recurrently.
651+ ///
652+ private func isPluginActive( _ plugin: String , completion: @escaping ( Bool ) -> ( Void ) ) {
653+ guard arePluginsSynced ( ) else {
654+ DDLogError ( " ⚠️ SystemPlugins acceded without being in sync. " )
655+ ServiceLocator . analytics. track ( event: WooAnalyticsEvent . Orders. pluginsNotSyncedYet ( ) )
656+ return completion ( false )
657+ }
658+
659+ let action = SystemStatusAction . fetchSystemPlugin ( siteID: order. siteID, systemPluginName: plugin) { plugin in
660+ completion ( plugin? . active == true )
661+ }
662+ stores. dispatch ( action)
663+ }
664+
665+ /// Function that checks for any existing system plugin in the order's store.
666+ /// If there is none, we assume plugins are not synced because at least the`WooCommerce` plugin should be present.
667+ ///
668+ private func arePluginsSynced( ) -> Bool {
669+ let predicate = NSPredicate ( format: " siteID == %lld " , order. siteID)
670+ let resultsController = ResultsController < StorageSystemPlugin > ( storageManager: storageManager, matching: predicate, sortedBy: [ ] )
671+ try ? resultsController. performFetch ( )
672+ return !resultsController. isEmpty
673+ }
621674}
622675
623676// MARK: Definitions
0 commit comments