diff --git a/Experiments/Experiments/DefaultFeatureFlagService.swift b/Experiments/Experiments/DefaultFeatureFlagService.swift index 660b46b3210..08045ad8250 100644 --- a/Experiments/Experiments/DefaultFeatureFlagService.swift +++ b/Experiments/Experiments/DefaultFeatureFlagService.swift @@ -72,7 +72,7 @@ public struct DefaultFeatureFlagService: FeatureFlagService { case .pointOfSale: return buildConfig == .localDeveloper || buildConfig == .alpha case .enableCouponsInPointOfSale: - return buildConfig == .localDeveloper || buildConfig == .alpha + return false case .googleAdsCampaignCreationOnWebView: return true case .backgroundTasks: diff --git a/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift b/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift new file mode 100644 index 00000000000..9be0a37baae --- /dev/null +++ b/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift @@ -0,0 +1,34 @@ +import Observation +import enum Yosemite.POSItem +import protocol Yosemite.PointOfSaleItemServiceProtocol + +@available(iOS 17.0, *) +@Observable final class PointOfSaleCouponsController: PointOfSaleItemsControllerProtocol { + var itemsViewState: ItemsViewState = ItemsViewState(containerState: .loading, + itemsStack: ItemsStackState(root: .loading([]), + itemStates: [:])) + private let paginationTracker: AsyncPaginationTracker + private var childPaginationTrackers: [POSItem: AsyncPaginationTracker] = [:] + private let itemProvider: PointOfSaleItemServiceProtocol + + init(itemProvider: PointOfSaleItemServiceProtocol) { + self.itemProvider = itemProvider + self.paginationTracker = .init() + } + + @MainActor + func loadItems(base: ItemListBaseItem) async { + debugPrint("🍍 CouponsController::loadItems called") + itemsViewState = ItemsViewState(containerState: .content, itemsStack: .init(root: .loaded([], hasMoreItems: false), itemStates: [:])) + } + + func refreshItems(base: ItemListBaseItem) async { + debugPrint("🍍 CouponsController::refreshItems called") + itemsViewState = ItemsViewState(containerState: .content, itemsStack: .init(root: .loaded([], hasMoreItems: false), itemStates: [:])) + } + + func loadNextItems(base: ItemListBaseItem) async { + debugPrint("🍍 CouponsController::loadNextItems called") + itemsViewState = ItemsViewState(containerState: .content, itemsStack: .init(root: .loaded([], hasMoreItems: false), itemStates: [:])) + } +} diff --git a/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift b/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift index 29ca6511cc0..faf58ef0013 100644 --- a/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift +++ b/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift @@ -6,8 +6,14 @@ import enum Yosemite.PointOfSaleItemServiceError import struct Yosemite.POSVariableParentProduct import class Yosemite.Store +enum ItemType { + case products + case coupons +} + @available(iOS 17.0, *) protocol PointOfSaleItemsControllerProtocol { + /// var itemsViewState: ItemsViewState { get } /// Loads the first page of items for a given base item. func loadItems(base: ItemListBaseItem) async @@ -17,6 +23,8 @@ protocol PointOfSaleItemsControllerProtocol { func loadNextItems(base: ItemListBaseItem) async } + + @available(iOS 17.0, *) @Observable final class PointOfSaleItemsController: PointOfSaleItemsControllerProtocol { var itemsViewState: ItemsViewState = ItemsViewState(containerState: .loading, @@ -158,14 +166,6 @@ protocol PointOfSaleItemsControllerProtocol { } } -@available(iOS 17.0, *) -private extension PointOfSaleItemsController { - func loadPointOfSaleCoupons() { - let posCoupons = itemProvider.providePointOfSaleCoupons() - debugPrint(posCoupons) - } -} - @available(iOS 17.0, *) private extension PointOfSaleItemsController { func setLoadingState(base: ItemListBaseItem) { @@ -202,6 +202,7 @@ private extension PointOfSaleItemsController { func fetchItems(pageNumber: Int, appendToExistingItems: Bool = true) async throws -> Bool { do { let pagedItems = try await itemProvider.providePointOfSaleItems(pageNumber: pageNumber) + let newItems = pagedItems.items var allItems = appendToExistingItems ? itemsViewState.itemsStack.root.items : [] let uniqueNewItems = newItems.filter { newItem in diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift index 6f65bedc293..d544b552e9f 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift @@ -53,14 +53,16 @@ protocol PointOfSaleAggregateModelProtocol { var cardPresentPaymentOnboardingViewModel: CardPresentPaymentsOnboardingViewModel? private var onOnboardingCancellation: (() -> Void)? - var itemsViewState: ItemsViewState { itemsController.itemsViewState } + var itemsViewState: ItemsViewState { currentController.itemsViewState } private(set) var cart: Cart = .init() var orderState: PointOfSaleOrderState { orderController.orderState.externalState } private var internalOrderState: PointOfSaleInternalOrderState { orderController.orderState } + private var currentController: PointOfSaleItemsControllerProtocol private let itemsController: PointOfSaleItemsControllerProtocol + private let couponsController: PointOfSaleItemsControllerProtocol private let cardPresentPaymentService: CardPresentPaymentFacade private let orderController: PointOfSaleOrderControllerProtocol @@ -73,12 +75,15 @@ protocol PointOfSaleAggregateModelProtocol { private var cancellables: Set = [] init(itemsController: PointOfSaleItemsControllerProtocol, + couponsController: PointOfSaleItemsControllerProtocol, cardPresentPaymentService: CardPresentPaymentFacade, orderController: PointOfSaleOrderControllerProtocol, analytics: Analytics = ServiceLocator.analytics, collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking, paymentState: PointOfSalePaymentState = .card(.idle)) { + self.currentController = itemsController // Default current controller set to products self.itemsController = itemsController + self.couponsController = couponsController self.cardPresentPaymentService = cardPresentPaymentService self.orderController = orderController self.analytics = analytics @@ -95,17 +100,23 @@ protocol PointOfSaleAggregateModelProtocol { extension PointOfSaleAggregateModel { @MainActor func loadItems(base: ItemListBaseItem) async { - await itemsController.loadItems(base: base) + await currentController.loadItems(base: base) } @MainActor func refreshItems(base: ItemListBaseItem) async { - await itemsController.refreshItems(base: base) + await currentController.refreshItems(base: base) } @MainActor func loadNextItems(base: ItemListBaseItem) async { - await itemsController.loadNextItems(base: base) + await currentController.loadNextItems(base: base) + } + + func switchToItemType(_ type: ItemType) async { + let newController = type == .products ? itemsController : couponsController + currentController = newController + await refreshItems(base: .root) } } diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/CardReaderConnectionStatusView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/CardReaderConnectionStatusView.swift index 188b4166fd3..3995afdc807 100644 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/CardReaderConnectionStatusView.swift +++ b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/CardReaderConnectionStatusView.swift @@ -171,6 +171,7 @@ private extension CardReaderConnectionStatusView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics() diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift index 5ca3d568be8..4badbe5dd11 100644 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift +++ b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift @@ -125,6 +125,7 @@ private extension PointOfSalePaymentSuccessView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/CartView.swift b/WooCommerce/Classes/POS/Presentation/CartView.swift index 0cdd312b0c1..402b2112b4a 100644 --- a/WooCommerce/Classes/POS/Presentation/CartView.swift +++ b/WooCommerce/Classes/POS/Presentation/CartView.swift @@ -302,6 +302,7 @@ private extension CartView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -313,6 +314,7 @@ private extension CartView { #Preview("Cart with one item") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift b/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift index 2d70f9d11e0..c175229b059 100644 --- a/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift +++ b/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift @@ -147,6 +147,7 @@ private extension ChildItemList { ], hasMoreItems: false)])) let posModel = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -172,6 +173,7 @@ private extension ChildItemList { ])) let posModel = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift b/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift index 35fe9f862af..46af33e0d82 100644 --- a/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift +++ b/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift @@ -167,6 +167,7 @@ private extension ItemListRow { #Preview("Loading") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/ItemListView.swift b/WooCommerce/Classes/POS/Presentation/ItemListView.swift index d197d124e09..cec11ae7185 100644 --- a/WooCommerce/Classes/POS/Presentation/ItemListView.swift +++ b/WooCommerce/Classes/POS/Presentation/ItemListView.swift @@ -1,5 +1,7 @@ import SwiftUI import enum Yosemite.POSItem +import protocol Yosemite.POSOrderableItem +import struct Yosemite.POSCoupon @available(iOS 17.0, *) struct ItemListView: View { @@ -15,6 +17,12 @@ struct ItemListView: View { @AppStorage(BannerState.isSimpleProductsOnlyBannerDismissedKey) private var isHeaderBannerDismissed: Bool = false + private var shouldShowCoupons: Bool { + ServiceLocator.featureFlagService.isFeatureFlagEnabled(.enableCouponsInPointOfSale) + } + + @State private var selectedItemType: ItemType = .products + var body: some View { if #available(iOS 18.0, *) { NavigationStack { @@ -32,6 +40,21 @@ struct ItemListView: View { var content: some View { VStack { headerView + + HStack { + Button(action: { + displayItemType(.products) + }, label: { + Text("Products") + }) + Button(action: { + displayItemType(.coupons) + }, label: { + Text("Coupons") + }) + } + .renderedIf(shouldShowCoupons) + switch itemListState { case .loading(let items), .loaded(let items, _), @@ -138,6 +161,13 @@ private extension ItemListView { var shouldShowHeaderBanner: Bool { itemListState.eligibleToShowSimpleProductsBanner && !isHeaderBannerDismissed } + + func displayItemType(_ itemType: ItemType) { + selectedItemType = itemType + Task { @MainActor in + await posModel.switchToItemType(itemType) + } + } } private extension ItemListState { @@ -222,6 +252,7 @@ private extension ItemListView { } let posModel = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -233,6 +264,7 @@ private extension ItemListView { #Preview("Loading") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift b/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift index a3dcbcc9819..aee962956d1 100644 --- a/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift +++ b/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift @@ -139,6 +139,7 @@ private extension POSFloatingControlView { #Preview("Reader Disconnected") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -152,6 +153,7 @@ private extension POSFloatingControlView { let paymentService = CardPresentPaymentPreviewService() let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -165,6 +167,7 @@ private extension POSFloatingControlView { #Preview("Secondary/disabled Background") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/PaymentButtons.swift b/WooCommerce/Classes/POS/Presentation/PaymentButtons.swift index 156b880541f..dd454101a62 100644 --- a/WooCommerce/Classes/POS/Presentation/PaymentButtons.swift +++ b/WooCommerce/Classes/POS/Presentation/PaymentButtons.swift @@ -90,6 +90,7 @@ private extension PaymentsActionButtons { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift index 3753e88e8c8..d674bc9875c 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift @@ -199,6 +199,7 @@ private extension PointOfSaleCollectCashView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift index 843ed164271..a0fe45a0d66 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift @@ -191,6 +191,7 @@ private extension PointOfSaleDashboardView { #Preview("Container loading state") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -206,6 +207,7 @@ private extension PointOfSaleDashboardView { let itemsController = PointOfSalePreviewItemsController() let posModel = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift index 2e4f53ce5d9..d7365c2677a 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift @@ -8,11 +8,13 @@ struct PointOfSaleEntryPointView: View { private let onPointOfSaleModeActiveStateChange: ((Bool) -> Void) private let itemsController: PointOfSaleItemsControllerProtocol + private let couponsController: PointOfSaleItemsControllerProtocol private let cardPresentPaymentService: CardPresentPaymentFacade private let orderController: PointOfSaleOrderControllerProtocol private let collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking init(itemsController: PointOfSaleItemsControllerProtocol, + couponsController: PointOfSaleItemsControllerProtocol, onPointOfSaleModeActiveStateChange: @escaping ((Bool) -> Void), cardPresentPaymentService: CardPresentPaymentFacade, orderController: PointOfSaleOrderControllerProtocol, @@ -20,6 +22,7 @@ struct PointOfSaleEntryPointView: View { self.onPointOfSaleModeActiveStateChange = onPointOfSaleModeActiveStateChange self.itemsController = itemsController + self.couponsController = couponsController self.cardPresentPaymentService = cardPresentPaymentService self.orderController = orderController self.collectOrderPaymentAnalyticsTracker = collectOrderPaymentAnalyticsTracker @@ -40,6 +43,7 @@ struct PointOfSaleEntryPointView: View { // See https://developer.apple.com/documentation/swiftui/state#Store-observable-objects for details. posModel = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: couponsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, collectOrderPaymentAnalyticsTracker: collectOrderPaymentAnalyticsTracker) @@ -60,6 +64,7 @@ struct PointOfSaleEntryPointView: View { @available(iOS 17.0, *) #Preview { PointOfSaleEntryPointView(itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), onPointOfSaleModeActiveStateChange: { _ in }, cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), diff --git a/WooCommerce/Classes/POS/Presentation/Reusable Views/POSSendReceiptView.swift b/WooCommerce/Classes/POS/Presentation/Reusable Views/POSSendReceiptView.swift index 63ecc1ac9f6..899d7c43cce 100644 --- a/WooCommerce/Classes/POS/Presentation/Reusable Views/POSSendReceiptView.swift +++ b/WooCommerce/Classes/POS/Presentation/Reusable Views/POSSendReceiptView.swift @@ -160,6 +160,7 @@ private extension POSSendReceiptView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/TotalsView.swift b/WooCommerce/Classes/POS/Presentation/TotalsView.swift index ae434d7e935..62c9b028a43 100644 --- a/WooCommerce/Classes/POS/Presentation/TotalsView.swift +++ b/WooCommerce/Classes/POS/Presentation/TotalsView.swift @@ -440,6 +440,7 @@ private extension TotalsView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewItemsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift index 5d15af6a027..e05f6639d74 100644 --- a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift +++ b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift @@ -56,10 +56,6 @@ final class PointOfSalePreviewItemService: PointOfSaleItemServiceProtocol { name: "Product 1", formattedPrice: "$1.00") } - - func providePointOfSaleCoupons() -> [POSItem] { - [] - } } @available(iOS 17.0, *) diff --git a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenu.swift b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenu.swift index ad0ccaedc7d..ff054b558c8 100644 --- a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenu.swift +++ b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenu.swift @@ -40,6 +40,7 @@ struct HubMenu: View { #available(iOS 17.0, *) { PointOfSaleEntryPointView( itemsController: PointOfSaleItemsController(itemProvider: viewModel.posItemProvider), + couponsController: PointOfSaleCouponsController(itemProvider: viewModel.posCouponProvider), onPointOfSaleModeActiveStateChange: { isEnabled in viewModel.updateDefaultConfigurationForPointOfSale(isEnabled) }, diff --git a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewModel.swift b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewModel.swift index f9ec4f2cf57..a826298e2a5 100644 --- a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewModel.swift @@ -104,6 +104,16 @@ final class HubMenuViewModel: ObservableObject { storage: storage) }() + private(set) lazy var posCouponProvider: PointOfSaleItemServiceProtocol = { + let storage = ServiceLocator.storageManager + let currencySettings = ServiceLocator.currencySettings + + return PointOfSaleCouponService(siteID: siteID, + currencySettings: currencySettings, + credentials: credentials, + storage: storage) + }() + private(set) lazy var inboxViewModel = InboxViewModel(siteID: siteID) @Published private(set) var shouldShowNewFeatureBadgeOnPayments: Bool = false diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 8d2f3d20d50..b74a171e660 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -1607,6 +1607,7 @@ 68A905012ACCFC13004C71D3 /* CollapsibleProductCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68A905002ACCFC13004C71D3 /* CollapsibleProductCard.swift */; }; 68AC9D292ACE598B0042F784 /* ProductImageThumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68AC9D282ACE598B0042F784 /* ProductImageThumbnail.swift */; }; 68AF3C3B2D01481C006F1ED2 /* POSReceiptEligibilityBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68AF3C3A2D01481A006F1ED2 /* POSReceiptEligibilityBanner.swift */; }; + 68B681162D9257810098D5CD /* PointOfSaleCouponsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B681152D92577F0098D5CD /* PointOfSaleCouponsController.swift */; }; 68B6F22B2ADE7ED500D171FC /* TooltipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B6F22A2ADE7ED500D171FC /* TooltipView.swift */; }; 68C31B712A8617C500AE5C5A /* NewNoteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68C31B702A8617C500AE5C5A /* NewNoteViewModel.swift */; }; 68C53CBE2C1FE59B00C6D80B /* ItemListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68C53CBD2C1FE59B00C6D80B /* ItemListView.swift */; }; @@ -4784,6 +4785,7 @@ 68A905002ACCFC13004C71D3 /* CollapsibleProductCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleProductCard.swift; sourceTree = ""; }; 68AC9D282ACE598B0042F784 /* ProductImageThumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductImageThumbnail.swift; sourceTree = ""; }; 68AF3C3A2D01481A006F1ED2 /* POSReceiptEligibilityBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSReceiptEligibilityBanner.swift; sourceTree = ""; }; + 68B681152D92577F0098D5CD /* PointOfSaleCouponsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCouponsController.swift; sourceTree = ""; }; 68B6F22A2ADE7ED500D171FC /* TooltipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipView.swift; sourceTree = ""; }; 68C31B702A8617C500AE5C5A /* NewNoteViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewNoteViewModel.swift; sourceTree = ""; }; 68C53CBD2C1FE59B00C6D80B /* ItemListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListView.swift; sourceTree = ""; }; @@ -8199,6 +8201,7 @@ 200BA1572CF092150006DC5B /* Controllers */ = { isa = PBXGroup; children = ( + 68B681152D92577F0098D5CD /* PointOfSaleCouponsController.swift */, 200BA1582CF092280006DC5B /* PointOfSaleItemsController.swift */, 20CF75B92CF4E69000ACCF4A /* PointOfSaleOrderController.swift */, ); @@ -16760,6 +16763,7 @@ 268EC46126D3F67800716F5C /* EditCustomerNoteViewModel.swift in Sources */, DE5746362B4522ED0034B10D /* BlazeBudgetSettingViewModel.swift in Sources */, B555530D21B57DC300449E71 /* UserNotificationsCenterAdapter.swift in Sources */, + 68B681162D9257810098D5CD /* PointOfSaleCouponsController.swift in Sources */, 45C8B2662316AB460002FA77 /* BillingAddressTableViewCell.swift in Sources */, 26BCA0422C35EDBF000BE96C /* OrderListSyncBackgroundTask.swift in Sources */, E11228BC2707161E004E9F2D /* CardPresentModalUpdateFailed.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/POS/Mocks/MockPOSItemProvider.swift b/WooCommerce/WooCommerceTests/POS/Mocks/MockPOSItemProvider.swift index 729a4cc7f65..f4e4bb1e942 100644 --- a/WooCommerce/WooCommerceTests/POS/Mocks/MockPOSItemProvider.swift +++ b/WooCommerce/WooCommerceTests/POS/Mocks/MockPOSItemProvider.swift @@ -45,10 +45,6 @@ final class MockPointOfSaleItemService: PointOfSaleItemServiceProtocol { return .init(items: MockPointOfSaleItemService.makeInitialVariationItems(), hasMorePages: shouldSimulateTwoPagesOfVariations) } - - func providePointOfSaleCoupons() -> [Yosemite.POSItem] { - [] - } } extension MockPointOfSaleItemService { diff --git a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift index 971403d8758..a3eb5a1142d 100644 --- a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift @@ -13,6 +13,7 @@ struct PointOfSaleAggregateModelTests { @Test func inits_with_building_order_stage() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -25,6 +26,7 @@ struct PointOfSaleAggregateModelTests { @Test func startNewCart_removes_all_items_from_cart_and_moves_back_to_building() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -46,6 +48,7 @@ struct PointOfSaleAggregateModelTests { @Test func checkOut_moves_to_finalizing_order_stage() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -63,6 +66,7 @@ struct PointOfSaleAggregateModelTests { @Test func addMoreToCart_moves_to_building_order_stage() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -93,6 +97,7 @@ struct PointOfSaleAggregateModelTests { @Test func addItem_results_in_a_non_empty_cart() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -111,6 +116,7 @@ struct PointOfSaleAggregateModelTests { @Test func addItem_puts_new_items_first_in_the_cart() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -128,6 +134,7 @@ struct PointOfSaleAggregateModelTests { @Test func removeItem_after_adding_two_items_removes_item_correctly() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -152,6 +159,7 @@ struct PointOfSaleAggregateModelTests { @Test func removeAllItemsFromCart_removes_everything() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -180,6 +188,7 @@ struct PointOfSaleAggregateModelTests { func addToCart_tracks_analytics_event() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -209,6 +218,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -229,6 +239,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -252,6 +263,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -274,6 +286,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -296,6 +309,7 @@ struct PointOfSaleAggregateModelTests { // Given let orderController = MockPointOfSaleOrderController() let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -316,6 +330,7 @@ struct PointOfSaleAggregateModelTests { let expectedError = NSError(domain: "some error", code: -1) let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -338,6 +353,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -365,6 +381,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -380,6 +397,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), @@ -398,6 +416,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -419,6 +438,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), @@ -437,6 +457,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -461,6 +482,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -480,6 +502,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -498,6 +521,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -518,6 +542,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -548,6 +573,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -570,6 +596,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -598,6 +625,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -629,6 +657,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -659,6 +688,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -679,6 +709,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -699,6 +730,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -730,6 +762,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -756,6 +789,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -792,6 +826,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: analytics, @@ -821,6 +856,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: analytics, @@ -841,6 +877,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: analytics, @@ -859,6 +896,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: analytics, @@ -876,6 +914,7 @@ struct PointOfSaleAggregateModelTests { // Given let analyticsTracker = MockPOSCollectOrderPaymentAnalyticsTracker() let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, collectOrderPaymentAnalyticsTracker: analyticsTracker) @@ -892,6 +931,7 @@ struct PointOfSaleAggregateModelTests { // Given let analyticsTracker = MockPOSCollectOrderPaymentAnalyticsTracker() let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -909,6 +949,7 @@ struct PointOfSaleAggregateModelTests { let mockAnalyticsProvider = MockAnalyticsProvider() let mockAnalytics = WooAnalytics(analyticsProvider: mockAnalyticsProvider) let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: mockAnalytics, diff --git a/Yosemite/Yosemite.xcodeproj/project.pbxproj b/Yosemite/Yosemite.xcodeproj/project.pbxproj index a414f3bbbcf..16bbf41d7c1 100644 --- a/Yosemite/Yosemite.xcodeproj/project.pbxproj +++ b/Yosemite/Yosemite.xcodeproj/project.pbxproj @@ -272,6 +272,7 @@ 6898F3742C0842150039F10A /* PointOfSaleItemServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6898F3732C0842150039F10A /* PointOfSaleItemServiceProtocol.swift */; }; 689D11D52891B9A400F6A83F /* WooFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 689D11D42891B9A400F6A83F /* WooFoundation.framework */; }; 68A70DD22D0BF6F60013B807 /* POSReceiptService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68A70DD12D0BF6F30013B807 /* POSReceiptService.swift */; }; + 68B681182D925B190098D5CD /* PointOfSaleCouponService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68B681172D925B170098D5CD /* PointOfSaleCouponService.swift */; }; 68BD37B528DB2E9800C2A517 /* CustomerStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68BD37B428DB2E9800C2A517 /* CustomerStore.swift */; }; 68BD37B928DB323D00C2A517 /* CustomerStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68BD37B828DB323D00C2A517 /* CustomerStoreTests.swift */; }; 68EA25342C08734900C49AE2 /* POSSimpleProduct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68EA25332C08734800C49AE2 /* POSSimpleProduct.swift */; }; @@ -813,6 +814,7 @@ 6898F3732C0842150039F10A /* PointOfSaleItemServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleItemServiceProtocol.swift; sourceTree = ""; }; 689D11D42891B9A400F6A83F /* WooFoundation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WooFoundation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 68A70DD12D0BF6F30013B807 /* POSReceiptService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSReceiptService.swift; sourceTree = ""; }; + 68B681172D925B170098D5CD /* PointOfSaleCouponService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCouponService.swift; sourceTree = ""; }; 68BD37B428DB2E9800C2A517 /* CustomerStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerStore.swift; sourceTree = ""; }; 68BD37B828DB323D00C2A517 /* CustomerStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerStoreTests.swift; sourceTree = ""; }; 68EA25332C08734800C49AE2 /* POSSimpleProduct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSSimpleProduct.swift; sourceTree = ""; }; @@ -1534,6 +1536,7 @@ 6898F3722C0840FE0039F10A /* PointOfSale */ = { isa = PBXGroup; children = ( + 68B681172D925B170098D5CD /* PointOfSaleCouponService.swift */, 6823533E2D82A90A00F24470 /* POSCoupon.swift */, 6898F3732C0842150039F10A /* PointOfSaleItemServiceProtocol.swift */, 68EA25332C08734800C49AE2 /* POSSimpleProduct.swift */, @@ -2345,6 +2348,7 @@ CECE6BBE2BA9DE3200A57C1F /* WCAnalyticsCustomer+ReadOnlyConvertible.swift in Sources */, 02FF054F23D983F30058E6E7 /* FileManager+URL.swift in Sources */, 03F3AFE728097D6400E328BE /* CardPresentPaymentsPlugin.swift in Sources */, + 68B681182D925B190098D5CD /* PointOfSaleCouponService.swift in Sources */, B52E0032211A440D00700FDE /* Order+ReadOnlyType.swift in Sources */, CE606D9C2BE3BCC4001CB424 /* ShippingMethod+ReadOnlyConvertible.swift in Sources */, 261F94E4242EFA6D00762B58 /* ProductCategoryAction.swift in Sources */, diff --git a/Yosemite/Yosemite/PointOfSale/PointOfSaleCouponService.swift b/Yosemite/Yosemite/PointOfSale/PointOfSaleCouponService.swift new file mode 100644 index 00000000000..fb00c40250e --- /dev/null +++ b/Yosemite/Yosemite/PointOfSale/PointOfSaleCouponService.swift @@ -0,0 +1,79 @@ +import protocol Networking.Network +import protocol Networking.ProductVariationsRemoteProtocol +import class Networking.ProductsRemote +import class Networking.ProductVariationsRemote +import class Networking.AlamofireNetwork +import class WooFoundation.CurrencyFormatter +import class WooFoundation.CurrencySettings +import Storage + +public final class PointOfSaleCouponService: PointOfSaleItemServiceProtocol { + private var siteID: Int64 + private let currencyFormatter: CurrencyFormatter + private let productsRemote: ProductsRemote + private let variationRemote: ProductVariationsRemoteProtocol + private let storage: StorageManagerType? + + public init(siteID: Int64, + currencySettings: CurrencySettings, + network: Network, + storage: StorageManagerType? = nil) { + self.siteID = siteID + self.currencyFormatter = CurrencyFormatter(currencySettings: currencySettings) + self.productsRemote = ProductsRemote(network: network) + self.variationRemote = ProductVariationsRemote(network: network) + self.storage = storage + } + + public convenience init(siteID: Int64, + currencySettings: CurrencySettings, + credentials: Credentials?, + storage: StorageManagerType) { + self.init(siteID: siteID, + currencySettings: currencySettings, + network: AlamofireNetwork(credentials: credentials), + storage: storage) + } + + // TODO: + // gh-15326 - Return PagedItems instead. + @MainActor + public func providePointOfSaleCoupons() -> [POSItem] { + guard let storage = storage else { + return [] + } + let predicate = NSPredicate(format: "siteID == %lld", siteID) + let descriptor = NSSortDescriptor(keyPath: \StorageCoupon.dateCreated, + ascending: false) + + let resultsController = ResultsController(storageManager: storage, + matching: predicate, + sortedBy: [descriptor]) + + do { + try resultsController.performFetch() + let storeCoupons = resultsController.fetchedObjects + return mapCouponsToPOSItems(coupons: storeCoupons) + } catch { + debugPrint(error) + return [] + } + } + + private func mapCouponsToPOSItems(coupons: [Coupon]) -> [POSItem] { + coupons.compactMap { coupon in + .coupon(POSCoupon(id: UUID(), code: coupon.code)) + } + } + + // TODO: Remove this conformance + public func providePointOfSaleVariationItems(for parentProduct: POSVariableParentProduct, pageNumber: Int) async throws -> PagedItems { + return .init(items: [], hasMorePages: false) + } + + @MainActor + public func providePointOfSaleItems(pageNumber: Int) async throws -> PagedItems { + let coupons = providePointOfSaleCoupons() + return .init(items: coupons, hasMorePages: false) + } +} diff --git a/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift b/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift index 4b9e13a6ec1..5113f5aabae 100644 --- a/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift +++ b/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift @@ -99,36 +99,6 @@ public final class PointOfSaleItemService: PointOfSaleItemServiceProtocol { } } - // TODO: - // gh-15326 - Return PagedItems instead. - public func providePointOfSaleCoupons() -> [POSItem] { - guard let storage = storage else { - return [] - } - let predicate = NSPredicate(format: "siteID == %lld", siteID) - let descriptor = NSSortDescriptor(keyPath: \StorageCoupon.dateCreated, - ascending: false) - - let resultsController = ResultsController(storageManager: storage, - matching: predicate, - sortedBy: [descriptor]) - - do { - try resultsController.performFetch() - let storeCoupons = resultsController.fetchedObjects - return mapCouponsToPOSItems(coupons: storeCoupons) - } catch { - debugPrint(error) - return [] - } - } - - private func mapCouponsToPOSItems(coupons: [Coupon]) -> [POSItem] { - coupons.compactMap { coupon in - .coupon(POSCoupon(id: UUID(), code: coupon.code)) - } - } - // Maps result to POSItem, and populate the output with: // - Formatted price based on store's currency settings. // - Product thumbnail, if any. diff --git a/Yosemite/Yosemite/PointOfSale/PointOfSaleItemServiceProtocol.swift b/Yosemite/Yosemite/PointOfSale/PointOfSaleItemServiceProtocol.swift index be69ba5915c..110212b14a9 100644 --- a/Yosemite/Yosemite/PointOfSale/PointOfSaleItemServiceProtocol.swift +++ b/Yosemite/Yosemite/PointOfSale/PointOfSaleItemServiceProtocol.swift @@ -58,7 +58,6 @@ public extension Sequence where Element == POSOrderableItem { public protocol PointOfSaleItemServiceProtocol { func providePointOfSaleItems(pageNumber: Int) async throws -> PagedItems func providePointOfSaleVariationItems(for parentProduct: POSVariableParentProduct, pageNumber: Int) async throws -> PagedItems - func providePointOfSaleCoupons() -> [POSItem] } // Default implementation for convenience, so we do not need to pass the first page explicitly