diff --git a/Modules/Sources/Yosemite/Stores/OrderCardPresentPaymentEligibilityStore.swift b/Modules/Sources/Yosemite/Stores/OrderCardPresentPaymentEligibilityStore.swift index 3ceec4f23e7..72b3c4ec90d 100644 --- a/Modules/Sources/Yosemite/Stores/OrderCardPresentPaymentEligibilityStore.swift +++ b/Modules/Sources/Yosemite/Stores/OrderCardPresentPaymentEligibilityStore.swift @@ -1,9 +1,29 @@ - import Foundation +import protocol Storage.StorageManagerType +import protocol NetworkingCore.Network /// Determines whether an order is eligible for card present payment or not /// public final class OrderCardPresentPaymentEligibilityStore: Store { + private let currentSite: () -> Site? + private lazy var siteCIABEligibilityChecker: CIABEligibilityCheckerProtocol = CIABEligibilityChecker( + currentSite: currentSite + ) + + public init( + dispatcher: Dispatcher, + storageManager: StorageManagerType, + network: Network, + currentSite: @escaping () -> Site? + ) { + self.currentSite = currentSite + super.init( + dispatcher: dispatcher, + storageManager: storageManager, + network: network + ) + } + /// Registers for supported Actions. /// override public func registerSupportedActions(in dispatcher: Dispatcher) { @@ -35,11 +55,27 @@ private extension OrderCardPresentPaymentEligibilityStore { cardPresentPaymentsConfiguration: CardPresentPaymentsConfiguration, onCompletion: (Result) -> Void) { let storage = storageManager.viewStorage + + guard let site = storage.loadSite(siteID: siteID)?.toReadOnly() else { + return onCompletion( + .failure( + OrderIsEligibleForCardPresentPaymentError.siteNotFoundInStorage + ) + ) + } + + guard siteCIABEligibilityChecker.isFeatureSupported(.cardReader, for: site) else { + return onCompletion( + .failure( + OrderIsEligibleForCardPresentPaymentError.cardReaderPaymentOptionIsNotSupportedForCIABSites + ) + ) + } + guard let order = storage.loadOrder(siteID: siteID, orderID: orderID)?.toReadOnly() else { return onCompletion(.failure(OrderIsEligibleForCardPresentPaymentError.orderNotFoundInStorage)) } - let orderProductsIDs = order.items.map(\.productID) let products = storage.loadProducts(siteID: siteID, productsIDs: orderProductsIDs).map { $0.toReadOnly() } @@ -50,5 +86,7 @@ private extension OrderCardPresentPaymentEligibilityStore { extension OrderCardPresentPaymentEligibilityStore { enum OrderIsEligibleForCardPresentPaymentError: Error { case orderNotFoundInStorage + case siteNotFoundInStorage + case cardReaderPaymentOptionIsNotSupportedForCIABSites } } diff --git a/WooCommerce/Classes/CIAB/CIABAffectedFeature.swift b/Modules/Sources/Yosemite/Tools/CIAB/CIABAffectedFeature.swift similarity index 84% rename from WooCommerce/Classes/CIAB/CIABAffectedFeature.swift rename to Modules/Sources/Yosemite/Tools/CIAB/CIABAffectedFeature.swift index 0d3a55059df..82e574b8079 100644 --- a/WooCommerce/Classes/CIAB/CIABAffectedFeature.swift +++ b/Modules/Sources/Yosemite/Tools/CIAB/CIABAffectedFeature.swift @@ -1,16 +1,15 @@ /// Describes the feature set affected by CIAB sites /// By the moment of introduction the features aren't supported by CIAB sites -/// periphery: ignore:all - Used through `.allCases` -enum CIABAffectedFeature: CaseIterable { +public enum CIABAffectedFeature: CaseIterable { case blaze case payments case splitShipments case groupedProducts case variableProducts - case giftCardEditing case productsStockDashboardCard case pointOfSale + case cardReader } extension CIABAffectedFeature { diff --git a/Modules/Sources/Yosemite/Tools/CIAB/CIABEligibilityChecker.swift b/Modules/Sources/Yosemite/Tools/CIAB/CIABEligibilityChecker.swift new file mode 100644 index 00000000000..3c2212c3a3a --- /dev/null +++ b/Modules/Sources/Yosemite/Tools/CIAB/CIABEligibilityChecker.swift @@ -0,0 +1,48 @@ +import Foundation + +public final class CIABEligibilityChecker { + public typealias ObtainSiteClosure = () -> Site? + + public let currentSite: ObtainSiteClosure + + public init(currentSite: @escaping ObtainSiteClosure) { + self.currentSite = currentSite + } +} + +extension CIABEligibilityChecker: CIABEligibilityCheckerProtocol { + public var isCurrentSiteCIAB: Bool { + guard let currentSite = currentSite() else { + return false + } + return isSiteCIAB(currentSite) + } + + public func isSiteCIAB(_ site: Site) -> Bool { + return site.isCIAB + } + + public func isFeatureSupportedForCurrentSite(_ feature: CIABAffectedFeature) -> Bool { + return !isCurrentSiteCIAB || !CIABAffectedFeature.unsupportedFeatures.contains(feature) + } + + public func isFeatureSupported( + _ feature: CIABAffectedFeature, + for site: Site + ) -> Bool { + return !isSiteCIAB(site) || !CIABAffectedFeature.unsupportedFeatures.contains(feature) + } +} + +// MARK: - Site checks + +private extension Site { + var isCIAB: Bool { + return isGarden && gardenName == GardenName.commerce.rawValue + } +} + +private enum GardenName: String { + /// Garden name for CIAB sites + case commerce +} diff --git a/WooCommerce/Classes/CIAB/CIABEligibilityCheckerProtocol.swift b/Modules/Sources/Yosemite/Tools/CIAB/CIABEligibilityCheckerProtocol.swift similarity index 79% rename from WooCommerce/Classes/CIAB/CIABEligibilityCheckerProtocol.swift rename to Modules/Sources/Yosemite/Tools/CIAB/CIABEligibilityCheckerProtocol.swift index 54c82d7f844..0d9393e05a9 100644 --- a/WooCommerce/Classes/CIAB/CIABEligibilityCheckerProtocol.swift +++ b/Modules/Sources/Yosemite/Tools/CIAB/CIABEligibilityCheckerProtocol.swift @@ -1,7 +1,7 @@ import Foundation -import Yosemite -protocol CIABEligibilityCheckerProtocol { +/// periphery: ignore +public protocol CIABEligibilityCheckerProtocol { var isCurrentSiteCIAB: Bool { get } func isSiteCIAB(_ site: Site) -> Bool diff --git a/Modules/Tests/YosemiteTests/Stores/OrderCardPresentPaymentEligibilityStoreTests.swift b/Modules/Tests/YosemiteTests/Stores/OrderCardPresentPaymentEligibilityStoreTests.swift index c8a73a5bc7b..444a60e7512 100644 --- a/Modules/Tests/YosemiteTests/Stores/OrderCardPresentPaymentEligibilityStoreTests.swift +++ b/Modules/Tests/YosemiteTests/Stores/OrderCardPresentPaymentEligibilityStoreTests.swift @@ -27,12 +27,26 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase { /// private var store: OrderCardPresentPaymentEligibilityStore! + private var currentSite: Site? + override func setUp() { super.setUp() dispatcher = Dispatcher() network = MockNetwork(useResponseQueue: true) storageManager = MockStorageManager() - store = OrderCardPresentPaymentEligibilityStore(dispatcher: dispatcher, storageManager: storageManager, network: network) + store = OrderCardPresentPaymentEligibilityStore( + dispatcher: dispatcher, + storageManager: storageManager, + network: network, + currentSite: { [weak self] in + return self?.currentSite + } + ) + } + + override func tearDown() { + currentSite = nil + super.tearDown() } // Other behavioural tests are in Order_CardPresentPaymentTests @@ -55,6 +69,14 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase { name: "Chocolate cake", productTypeKey: "simple") + let regularSite = Site.fake().copy( + siteID: sampleSiteID, + isGarden: false, + gardenName: nil + ) + self.currentSite = regularSite + + storageManager.insertSampleSite(readOnlySite: regularSite) storageManager.insertSampleProduct(readOnlyProduct: nonSubscriptionProduct) storageManager.insertSampleOrder(readOnlyOrder: cppEligibleOrder) @@ -75,4 +97,54 @@ final class OrderCardPresentPaymentEligibilityStoreTests: XCTestCase { let eligibility = try XCTUnwrap(result.get()) XCTAssertTrue(eligibility) } + + func test_orderIsEligibleForCardPresentPayment_returns_failure_for_CIAB_sites() throws { + // Given + let orderItem = OrderItem.fake().copy(itemID: 1234, + name: "Chocolate cake", + productID: 678, + quantity: 1.0) + let cppEligibleOrder = Order.fake().copy(siteID: sampleSiteID, + orderID: 111, + status: .pending, + currency: "USD", + datePaid: nil, + total: "5.00", + paymentMethodID: "woocommerce_payments", + items: [orderItem]) + let nonSubscriptionProduct = Product.fake().copy(siteID: sampleSiteID, + productID: 678, + name: "Chocolate cake", + productTypeKey: "simple") + + let ciabSite = Site.fake().copy( + siteID: sampleSiteID, + isGarden: true, + gardenName: "commerce" + ) + self.currentSite = ciabSite + + storageManager.insertSampleSite(readOnlySite: ciabSite) + storageManager.insertSampleProduct(readOnlyProduct: nonSubscriptionProduct) + storageManager.insertSampleOrder(readOnlyOrder: cppEligibleOrder) + + let configuration = CardPresentPaymentsConfiguration(country: .US) + + // When + let result = waitFor { promise in + let action = OrderCardPresentPaymentEligibilityAction + .orderIsEligibleForCardPresentPayment(orderID: 111, + siteID: self.sampleSiteID, + cardPresentPaymentsConfiguration: configuration) { result in + promise(result) + } + self.store.onAction(action) + } + + // Then + XCTAssertThrowsError(try result.get()) { error in + XCTAssertEqual(error as? OrderCardPresentPaymentEligibilityStore.OrderIsEligibleForCardPresentPaymentError, + .cardReaderPaymentOptionIsNotSupportedForCIABSites) + } + } } diff --git a/WooCommerce/Classes/CIAB/CIABEligibilityChecker.swift b/WooCommerce/Classes/CIAB/CIABEligibilityChecker.swift index 955af2262b0..07b37e5819b 100644 --- a/WooCommerce/Classes/CIAB/CIABEligibilityChecker.swift +++ b/WooCommerce/Classes/CIAB/CIABEligibilityChecker.swift @@ -1,47 +1,12 @@ import Foundation import Yosemite -final class CIABEligibilityChecker { - private let stores: StoresManager - - init(stores: StoresManager = ServiceLocator.stores) { - self.stores = stores - } -} - -extension CIABEligibilityChecker: CIABEligibilityCheckerProtocol { - var isCurrentSiteCIAB: Bool { - guard let currentSite = stores.sessionManager.defaultSite else { - return false - } - return isSiteCIAB(currentSite) - } - - func isSiteCIAB(_ site: Site) -> Bool { - return site.isCIAB - } - - func isFeatureSupportedForCurrentSite(_ feature: CIABAffectedFeature) -> Bool { - return !isCurrentSiteCIAB || !CIABAffectedFeature.unsupportedFeatures.contains(feature) +extension CIABEligibilityChecker { + convenience init() { + self.init( + currentSite: { + return ServiceLocator.stores.sessionManager.defaultSite + } + ) } - - func isFeatureSupported( - _ feature: CIABAffectedFeature, - for site: Site - ) -> Bool { - return !isSiteCIAB(site) || !CIABAffectedFeature.unsupportedFeatures.contains(feature) - } -} - -// MARK: - Site checks - -private extension Site { - var isCIAB: Bool { - return isGarden && gardenName == GardenName.commerce.rawValue - } -} - -private enum GardenName: String { - /// Garden name for CIAB sites - case commerce } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabVisibilityChecker.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabVisibilityChecker.swift index c7456827e39..9bff9ee1dec 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabVisibilityChecker.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabVisibilityChecker.swift @@ -11,6 +11,8 @@ import protocol Yosemite.StoresManager import class Yosemite.POSEligibilityService import enum Yosemite.FeatureFlagAction import class Yosemite.SiteAddress +import protocol Yosemite.CIABEligibilityCheckerProtocol +import class Yosemite.CIABEligibilityChecker import enum Yosemite.POSCountryCurrencyValidator final class POSTabVisibilityChecker: POSTabVisibilityCheckerProtocol { diff --git a/WooCommerce/Classes/Yosemite/AuthenticatedState.swift b/WooCommerce/Classes/Yosemite/AuthenticatedState.swift index 8efc471b60a..ec79983faf1 100644 --- a/WooCommerce/Classes/Yosemite/AuthenticatedState.swift +++ b/WooCommerce/Classes/Yosemite/AuthenticatedState.swift @@ -75,7 +75,14 @@ class AuthenticatedState: StoresManagerState { MediaStore(dispatcher: dispatcher, storageManager: storageManager, network: network), NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network), NotificationCountStore(dispatcher: dispatcher, storageManager: storageManager, fileStorage: PListFileStorage()), - OrderCardPresentPaymentEligibilityStore(dispatcher: dispatcher, storageManager: storageManager, network: network), + OrderCardPresentPaymentEligibilityStore( + dispatcher: dispatcher, + storageManager: storageManager, + network: network, + currentSite: { + ServiceLocator.stores.sessionManager.defaultSite + } + ), OrderNoteStore(dispatcher: dispatcher, storageManager: storageManager, network: network), OrderStore(dispatcher: dispatcher, storageManager: storageManager, network: network), OrderStatusStore(dispatcher: dispatcher, storageManager: storageManager, network: network), diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index b7663939daf..d6f5a021927 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -1013,9 +1013,7 @@ 2DB8916B2E27F6D90001B175 /* OrderListCellViewModel+Localizations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB8916A2E27F6CE0001B175 /* OrderListCellViewModel+Localizations.swift */; }; 2DB8916E2E27F7840001B175 /* OrderListCellViewModel+Localizations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB8916A2E27F6CE0001B175 /* OrderListCellViewModel+Localizations.swift */; }; 2DCB54FA2E6AE8E100621F90 /* CIABEligibilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DCB54F92E6AE8D800621F90 /* CIABEligibilityChecker.swift */; }; - 2DCB54FC2E6AFE6A00621F90 /* CIABAffectedFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DCB54FB2E6AFE6900621F90 /* CIABAffectedFeature.swift */; }; 2DE9DDFB2E6EF4A500155408 /* MockCIABEligibilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE9DDFA2E6EF4A300155408 /* MockCIABEligibilityChecker.swift */; }; - 2DE9DDFD2E6EF53C00155408 /* CIABEligibilityCheckerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE9DDFC2E6EF52E00155408 /* CIABEligibilityCheckerProtocol.swift */; }; 2DF0D1BC2E2907C100F8995C /* MarkOrderAsReadUseCase+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB88DA32E27DD790001B175 /* MarkOrderAsReadUseCase+Woo.swift */; }; 310D1B482734919E001D55B4 /* InPersonPaymentsLiveSiteInTestModeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 310D1B472734919E001D55B4 /* InPersonPaymentsLiveSiteInTestModeView.swift */; }; 311237EE2714DA240033C44E /* CardPresentModalDisplayMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 311237ED2714DA240033C44E /* CardPresentModalDisplayMessage.swift */; }; @@ -3897,9 +3895,7 @@ 2DB891682E27F61C0001B175 /* OrderListCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderListCellViewModel.swift; sourceTree = ""; }; 2DB8916A2E27F6CE0001B175 /* OrderListCellViewModel+Localizations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrderListCellViewModel+Localizations.swift"; sourceTree = ""; }; 2DCB54F92E6AE8D800621F90 /* CIABEligibilityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIABEligibilityChecker.swift; sourceTree = ""; }; - 2DCB54FB2E6AFE6900621F90 /* CIABAffectedFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIABAffectedFeature.swift; sourceTree = ""; }; 2DE9DDFA2E6EF4A300155408 /* MockCIABEligibilityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCIABEligibilityChecker.swift; sourceTree = ""; }; - 2DE9DDFC2E6EF52E00155408 /* CIABEligibilityCheckerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIABEligibilityCheckerProtocol.swift; sourceTree = ""; }; 310D1B472734919E001D55B4 /* InPersonPaymentsLiveSiteInTestModeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPersonPaymentsLiveSiteInTestModeView.swift; sourceTree = ""; }; 311237ED2714DA240033C44E /* CardPresentModalDisplayMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalDisplayMessage.swift; sourceTree = ""; }; 311D21E7264AEDB900102316 /* CardPresentModalScanningForReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalScanningForReader.swift; sourceTree = ""; }; @@ -7945,8 +7941,6 @@ 2DCB54F82E6AE8C900621F90 /* CIAB */ = { isa = PBXGroup; children = ( - 2DE9DDFC2E6EF52E00155408 /* CIABEligibilityCheckerProtocol.swift */, - 2DCB54FB2E6AFE6900621F90 /* CIABAffectedFeature.swift */, 2DCB54F92E6AE8D800621F90 /* CIABEligibilityChecker.swift */, ); path = CIAB; @@ -14166,7 +14160,6 @@ CE7F778B2C074D2500C89F4E /* EditableOrderShippingLineViewModel.swift in Sources */, B95A45E92A77AE2C0073A91F /* CustomerSelectorViewModel.swift in Sources */, 024DF3072372C18D006658FE /* AztecUIConfigurator.swift in Sources */, - 2DCB54FC2E6AFE6A00621F90 /* CIABAffectedFeature.swift in Sources */, EE1B07F32C81CB4B006D9769 /* BlazeLocalNotificationScheduler.swift in Sources */, DE02ABBE2B578D0E008E0AC4 /* CreditCardType.swift in Sources */, 020BE74823B05CF2007FE54C /* ProductInventoryEditableData.swift in Sources */, @@ -14933,7 +14926,6 @@ B626C71B287659D60083820C /* CustomFieldsListView.swift in Sources */, 02ECD1E624FFB4E900735BE5 /* ProductFactory.swift in Sources */, 260520F42B87BA23005D5D59 /* WooAnalyticsEvent+ConnectivityTool.swift in Sources */, - 2DE9DDFD2E6EF53C00155408 /* CIABEligibilityCheckerProtocol.swift in Sources */, 579CDEFF274D7E7900E8903D /* StoreStatsUsageTracksEventEmitter.swift in Sources */, CEDBDA472B6BEF2E002047D4 /* AnalyticsWebReport.swift in Sources */, 314DC4BD268D158F00444C9E /* CardReaderSettingsKnownReadersProvider.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/ViewRelated/CIAB/CIABEligibilityCheckerTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/CIAB/CIABEligibilityCheckerTests.swift index 6c42affe685..8fa5a5791ec 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/CIAB/CIABEligibilityCheckerTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/CIAB/CIABEligibilityCheckerTests.swift @@ -3,20 +3,23 @@ import XCTest @testable import Yosemite class CIABEligibilityCheckerTests: XCTestCase { - private var storesManager: MockStoresManager! private var sessionManager: SessionManager! private var checker: CIABEligibilityChecker! override func setUp() { super.setUp() sessionManager = .makeForTesting() - storesManager = MockStoresManager(sessionManager: sessionManager) - checker = CIABEligibilityChecker(stores: storesManager) + checker = CIABEligibilityChecker( + currentSite: { + return MockStoresManager( + sessionManager: self.sessionManager + ).sessionManager.defaultSite + } + ) } override func tearDown() { checker = nil - storesManager = nil sessionManager = nil super.tearDown() }