diff --git a/WooCommerce/Classes/POS/Controllers/PointOfSaleOrderController.swift b/WooCommerce/Classes/POS/Controllers/PointOfSaleOrderController.swift index ba128eafb77..0f01b2626e1 100644 --- a/WooCommerce/Classes/POS/Controllers/PointOfSaleOrderController.swift +++ b/WooCommerce/Classes/POS/Controllers/PointOfSaleOrderController.swift @@ -27,7 +27,7 @@ protocol PointOfSaleOrderControllerProtocol { var orderState: PointOfSaleInternalOrderState { get } @discardableResult - func syncOrder(for cartProducts: [CartItem], retryHandler: @escaping () async -> Void) async -> Result + func syncOrder(for cart: Cart, retryHandler: @escaping () async -> Void) async -> Result func sendReceipt(recipientEmail: String) async throws func clearOrder() func collectCashPayment() async throws @@ -63,9 +63,9 @@ protocol PointOfSaleOrderControllerProtocol { private var order: Order? = nil @MainActor @discardableResult - func syncOrder(for cartItems: [CartItem], + func syncOrder(for cart: Cart, retryHandler: @escaping () async -> Void) async -> Result { - let posCartItems = cartItems.map { + let posCartItems = cart.items.map { POSCartItem(item: $0.item, quantity: Decimal($0.quantity)) } diff --git a/WooCommerce/Classes/POS/Models/Cart.swift b/WooCommerce/Classes/POS/Models/Cart.swift new file mode 100644 index 00000000000..0ec2c437797 --- /dev/null +++ b/WooCommerce/Classes/POS/Models/Cart.swift @@ -0,0 +1,62 @@ +import Foundation +import protocol Yosemite.POSOrderableItem +import enum Yosemite.POSItem + +struct Cart { + var items: [CartItem] = [] + var coupons: [CartCouponItem] = [] +} + +struct CartItem { + let id: UUID + let item: POSOrderableItem + let title: String + let subtitle: String? + let quantity: Int +} + +struct CartCouponItem { + let id: UUID + let code: String +} + +// MARK: - Helper Methods + +extension Cart { + mutating func add(_ posItem: POSItem) { + switch posItem { + case .simpleProduct(let simpleProduct): + let productItem = CartItem(id: UUID(), item: simpleProduct, title: simpleProduct.name, subtitle: nil, quantity: 1) + items.insert(productItem, at: items.startIndex) + case .variation(let variation): + let productItem = CartItem(id: UUID(), item: variation, title: variation.parentProductName, subtitle: variation.name, quantity: 1) + items.insert(productItem, at: items.startIndex) + case .variableParentProduct: + return + case .coupon(let coupon): + let couponItem = CartCouponItem(id: UUID(), code: coupon.code) + coupons.insert(couponItem, at: coupons.startIndex) + } + } + + mutating func remove(_ cartItem: CartItem) { + items.removeAll { $0.id == cartItem.id } + } + + mutating func remove(_ cartCouponItem: CartCouponItem) { + coupons.removeAll { $0.id == cartCouponItem.id } + } + + mutating func removeAll() { + items.removeAll() + coupons.removeAll() + } + + var isEmpty: Bool { + items.isEmpty && coupons.isEmpty + } + + var isNotEmpty: Bool { + return !isEmpty + } +} diff --git a/WooCommerce/Classes/POS/Models/CartItem.swift b/WooCommerce/Classes/POS/Models/CartItem.swift deleted file mode 100644 index ee09f298ebd..00000000000 --- a/WooCommerce/Classes/POS/Models/CartItem.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Foundation -import protocol Yosemite.POSOrderableItem - -struct CartItem { - let id: UUID - let item: POSOrderableItem - let title: String - let subtitle: String? - let quantity: Int -} diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift index 1b2e86d038f..6f65bedc293 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift @@ -6,7 +6,7 @@ import protocol Yosemite.POSOrderableItem import protocol WooFoundation.Analytics import struct Yosemite.Order import struct Yosemite.OrderItem -import struct Yosemite.POSCartItem +import struct Yosemite.POSCoupon import enum Yosemite.POSItem import enum Yosemite.SystemStatusAction @@ -28,9 +28,10 @@ protocol PointOfSaleAggregateModelProtocol { func loadItems(base: ItemListBaseItem) async func loadNextItems(base: ItemListBaseItem) async - var cart: [CartItem] { get } + var cart: Cart { get } func addToCart(_ item: POSItem) func remove(cartItem: CartItem) + func remove(cartCouponItem: CartCouponItem) func removeAllItemsFromCart() func addMoreToCart() func startNewCart() @@ -54,7 +55,7 @@ protocol PointOfSaleAggregateModelProtocol { var itemsViewState: ItemsViewState { itemsController.itemsViewState } - private(set) var cart: [CartItem] = [] + private(set) var cart: Cart = .init() var orderState: PointOfSaleOrderState { orderController.orderState.externalState } private var internalOrderState: PointOfSaleInternalOrderState { orderController.orderState } @@ -108,33 +109,19 @@ extension PointOfSaleAggregateModel { } } -// MARK: - Cart - -private extension POSItem { - var cartItem: CartItem? { - switch self { - case .simpleProduct(let simpleProduct): - return CartItem(id: UUID(), item: simpleProduct, title: simpleProduct.name, subtitle: nil, quantity: 1) - case .variation(let variation): - return CartItem(id: UUID(), item: variation, title: variation.parentProductName, subtitle: variation.name, quantity: 1) - case .variableParentProduct: - return nil - case .coupon: - return nil - } - } -} - @available(iOS 17.0, *) extension PointOfSaleAggregateModel { func addToCart(_ item: POSItem) { trackCustomerInteractionStarted() - guard let cartItem = item.cartItem else { return } - cart.insert(cartItem, at: cart.startIndex) + cart.add(item) } func remove(cartItem: CartItem) { - cart.removeAll(where: { $0.id == cartItem.id } ) + cart.remove(cartItem) + } + + func remove(cartCouponItem: CartCouponItem) { + cart.remove(cartCouponItem) } func removeAllItemsFromCart() { @@ -164,7 +151,7 @@ private extension PointOfSaleAggregateModel { func trackCustomerInteractionStarted() { // At the moment we're assumming that an interaction starts simply when the cart is zero // but a more complex logic will be needed for other cases - if cart.count == 0 { + if cart.isEmpty { collectOrderPaymentAnalyticsTracker.trackCustomerInteractionStarted() } } diff --git a/WooCommerce/Classes/POS/Presentation/CartView.swift b/WooCommerce/Classes/POS/Presentation/CartView.swift index f4b6b43204b..0cdd312b0c1 100644 --- a/WooCommerce/Classes/POS/Presentation/CartView.swift +++ b/WooCommerce/Classes/POS/Presentation/CartView.swift @@ -16,13 +16,17 @@ struct CartView: View { @State private var shouldShowItemImages: Bool = false + private var shouldShowCoupons: Bool { + ServiceLocator.featureFlagService.isFeatureFlagEnabled(.enableCouponsInPointOfSale) + } + var body: some View { VStack { POSPageHeaderView(title: Localization.cartTitle, backButtonConfiguration: backButtonConfiguration, trailingContent: { DynamicHStack(horizontalAlignment: .trailing, verticalAlignment: .center, spacing: Constants.cartHeaderElementSpacing) { - if let itemsInCartLabel = viewHelper.itemsInCartLabel(for: posModel.cart.count) { + if let itemsInCartLabel = viewHelper.itemsInCartLabel(for: posModel.cart.items.count) { Text(itemsInCartLabel) .font(Constants.itemsFont) .lineLimit(1) @@ -47,7 +51,11 @@ struct CartView: View { ScrollViewReader { proxy in ScrollView { VStack(spacing: Constants.cartItemSpacing) { - ForEach(posModel.cart, id: \.id) { cartItem in + if shouldShowCoupons { + couponsCartSectionView + } + + ForEach(posModel.cart.items, id: \.id) { cartItem in ItemRowView(cartItem: cartItem, showImage: $shouldShowItemImages, onItemRemoveTapped: posModel.orderStage == .building ? { @@ -58,7 +66,8 @@ struct CartView: View { .transition(.opacity) } } - .animation(Constants.cartAnimation, value: posModel.cart.map(\.id)) + .animation(Constants.cartAnimation, value: posModel.cart.items.map(\.id)) + .animation(Constants.cartAnimation, value: posModel.cart.coupons.map(\.id)) .background(GeometryReader { geometry in Color.clear.preference(key: ScrollOffSetPreferenceKey.self, value: geometry.frame(in: coordinateSpace).origin.y) @@ -81,7 +90,7 @@ struct CartView: View { .renderedIf(posModel.orderStage == .finalizing) } .coordinateSpace(name: Constants.scrollViewCoordinateSpaceIdentifier) - .onChange(of: posModel.cart.first?.id) { itemToScrollTo in + .onChange(of: posModel.cart.items.first?.id) { itemToScrollTo in if posModel.orderStage == .building { withAnimation { proxy.scrollTo(itemToScrollTo) @@ -93,7 +102,7 @@ struct CartView: View { Spacer() switch posModel.orderStage { case .building: - if posModel.cart.isEmpty { + if posModel.cart.items.isEmpty { EmptyView() } else { checkoutButton @@ -263,12 +272,27 @@ private extension CartView { } .background(backgroundColor.ignoresSafeArea(.all)) } + + var couponsCartSectionView: some View { + VStack { + ForEach(posModel.cart.coupons, id: \.id) { couponItem in + CouponRowView(couponItem: couponItem, + onItemRemoveTapped: posModel.orderStage == .building ? { + posModel.remove(cartCouponItem: couponItem) + } : nil) + .id(couponItem.id) + .transition(.opacity) + } + + Spacer(minLength: 48) + } + } } @available(iOS 17.0, *) private extension CartView { func trackCheckoutTapped() { - let itemsInCart = posModel.cart.count + let itemsInCart = posModel.cart.items.count ServiceLocator.analytics.track(event: .PointOfSale.checkoutTapped(itemsInCart)) } } diff --git a/WooCommerce/Classes/POS/Presentation/CouponRowView.swift b/WooCommerce/Classes/POS/Presentation/CouponRowView.swift new file mode 100644 index 00000000000..73f1fb6c048 --- /dev/null +++ b/WooCommerce/Classes/POS/Presentation/CouponRowView.swift @@ -0,0 +1,65 @@ +import SwiftUI + +struct CouponRowView: View { + private let couponItem: CartCouponItem + private let onItemRemoveTapped: (() -> Void)? + + @ScaledMetric private var scale: CGFloat = 1.0 + + init(couponItem: CartCouponItem, onItemRemoveTapped: (() -> Void)? = nil) { + self.couponItem = couponItem + self.onItemRemoveTapped = onItemRemoveTapped + } + + var body: some View { + HStack(spacing: Constants.horizontalElementSpacing) { + Rectangle() + .foregroundColor(.posSurfaceDim) + .overlay { + Text(Image(systemName: "tag.square.fill")) + .font(.posButtonSymbolLarge) + .foregroundColor(.posOnSurfaceVariantLowest) + } + .frame(width: Constants.couponCardSize, height: Constants.couponCardSize) + + VStack(alignment: .leading) { + Text(couponItem.code) + .foregroundColor(PointOfSaleItemListCardConstants.titleColor) + .font(Constants.itemTitleFont) + } + .frame(maxWidth: .infinity, alignment: .leading) + + if let onItemRemoveTapped { + Button(action: { + onItemRemoveTapped() + }, label: { + Text(Image(systemName: "xmark.circle")) + .font(.posButtonSymbolMedium) + }) + .foregroundColor(Color.posOnSurfaceVariantLowest) + } + } + .padding(.trailing, Constants.cardContentHorizontalPadding) + .frame(maxWidth: .infinity, idealHeight: Constants.couponCardSize * scale) + .background(Color.posSurfaceContainerLowest) + .posItemCardBorderStyles() + .padding(.horizontal, Constants.horizontalPadding) + } +} + +private extension CouponRowView { + enum Constants { + static let couponCardSize: CGFloat = 96 + static let horizontalPadding: CGFloat = POSPadding.medium + static let horizontalElementSpacing: CGFloat = POSSpacing.medium + static let cardContentHorizontalPadding: CGFloat = POSPadding.medium + static let itemTitleFont: POSFontStyle = .posBodySmallBold + } +} + +#if DEBUG +@available(iOS 17.0, *) +#Preview(traits: .sizeThatFitsLayout) { + CouponRowView(couponItem: CartCouponItem(id: UUID(), code: "10-Discount")) {} +} +#endif diff --git a/WooCommerce/Classes/POS/Presentation/ItemListView.swift b/WooCommerce/Classes/POS/Presentation/ItemListView.swift index 808cbcadffa..d197d124e09 100644 --- a/WooCommerce/Classes/POS/Presentation/ItemListView.swift +++ b/WooCommerce/Classes/POS/Presentation/ItemListView.swift @@ -1,6 +1,5 @@ import SwiftUI import enum Yosemite.POSItem -import protocol Yosemite.POSOrderableItem @available(iOS 17.0, *) struct ItemListView: View { diff --git a/WooCommerce/Classes/POS/Utils/PointOfSalePreviewOrderController.swift b/WooCommerce/Classes/POS/Utils/PointOfSalePreviewOrderController.swift index df5536dddd0..c73b9053ade 100644 --- a/WooCommerce/Classes/POS/Utils/PointOfSalePreviewOrderController.swift +++ b/WooCommerce/Classes/POS/Utils/PointOfSalePreviewOrderController.swift @@ -12,7 +12,7 @@ class PointOfSalePreviewOrderController: PointOfSaleOrderControllerProtocol { OrderFactory.newOrder(currency: .USD) ) - func syncOrder(for cartProducts: [CartItem], retryHandler: @escaping () async -> Void) async -> Result { + func syncOrder(for cart: Cart, retryHandler: @escaping () async -> Void) async -> Result { return .success(.newOrder) } diff --git a/WooCommerce/Classes/POS/ViewHelpers/CartViewHelper.swift b/WooCommerce/Classes/POS/ViewHelpers/CartViewHelper.swift index 736295dddeb..9a5ed3a44e1 100644 --- a/WooCommerce/Classes/POS/ViewHelpers/CartViewHelper.swift +++ b/WooCommerce/Classes/POS/ViewHelpers/CartViewHelper.swift @@ -18,7 +18,7 @@ final class CartViewHelper { return orderState.isSyncing } - func shouldShowClearCartButton(cart: [CartItem], orderStage: PointOfSaleOrderStage) -> Bool { + func shouldShowClearCartButton(cart: Cart, orderStage: PointOfSaleOrderStage) -> Bool { cart.isNotEmpty && orderStage == .building } } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/CardPresentPaymentsOnboardingViewModel.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/CardPresentPaymentsOnboardingViewModel.swift index c077b617d30..6f9ffd02911 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/CardPresentPaymentsOnboardingViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/In-Person Payments/CardPresentPaymentsOnboardingViewModel.swift @@ -45,10 +45,11 @@ final class CardPresentPaymentsOnboardingViewModel: ObservableObject, PaymentSet init( fixedState: CardPresentPaymentOnboardingState, fixedUserIsAdministrator: Bool = false, + useCase: CardPresentPaymentsOnboardingUseCaseProtocol = CardPresentPaymentsOnboardingUseCase(), stores: StoresManager = ServiceLocator.stores) { self.stores = stores state = fixedState - useCase = CardPresentPaymentsOnboardingUseCase() + self.useCase = useCase userIsAdministrator = fixedUserIsAdministrator updateLearnMoreURL(state: fixedState) } diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 432e74091c5..8d2f3d20d50 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 011D7A352CEC87B70007C187 /* CardPresentModalErrorEmailSent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D7A342CEC87B60007C187 /* CardPresentModalErrorEmailSent.swift */; }; 011DF3442C53A5CF000AFDD9 /* PointOfSaleCardPresentPaymentValidatingOrderMessageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011DF3432C53A5CF000AFDD9 /* PointOfSaleCardPresentPaymentValidatingOrderMessageViewModel.swift */; }; 011DF3462C53A919000AFDD9 /* PointOfSaleCardPresentPaymentActivityIndicatingMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011DF3452C53A919000AFDD9 /* PointOfSaleCardPresentPaymentActivityIndicatingMessageView.swift */; }; + 0139BB522D91B45800C78FDE /* CouponRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0139BB512D91B45500C78FDE /* CouponRowView.swift */; }; 013D2FB42CFEFEC600845D75 /* BuiltInCardReaderMerchantEducationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 013D2FB32CFEFEA800845D75 /* BuiltInCardReaderMerchantEducationPresenter.swift */; }; 013D2FB62CFF54BB00845D75 /* TapToPayEducationStepsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 013D2FB52CFF54B600845D75 /* TapToPayEducationStepsFactory.swift */; }; 014BD4B82C64E2BA0011A66E /* PointOfSaleOrderSyncErrorMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 014BD4B72C64E2BA0011A66E /* PointOfSaleOrderSyncErrorMessageView.swift */; }; @@ -1632,7 +1633,7 @@ 68E952D0287587BF0095A23D /* CardReaderManualRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E952CF287587BF0095A23D /* CardReaderManualRowView.swift */; }; 68E952D22875A44B0095A23D /* CardReaderType+Manual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68E952D12875A44B0095A23D /* CardReaderType+Manual.swift */; }; 68ED2BD62ADD2C8C00ECA88D /* LineDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68ED2BD52ADD2C8C00ECA88D /* LineDetailView.swift */; }; - 68F151E12C0DA7910082AEC8 /* CartItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68F151E02C0DA7910082AEC8 /* CartItem.swift */; }; + 68F151E12C0DA7910082AEC8 /* Cart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68F151E02C0DA7910082AEC8 /* Cart.swift */; }; 68F68A502D6730E200BB9568 /* POSCollectOrderPaymentAnalyticsTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68F68A4F2D6730DF00BB9568 /* POSCollectOrderPaymentAnalyticsTracking.swift */; }; 68F68A522D67365900BB9568 /* MockPOSCollectOrderPaymentAnalyticsTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68F68A512D67365900BB9568 /* MockPOSCollectOrderPaymentAnalyticsTracker.swift */; }; 68F896422D5E4323000B308B /* POSCollectOrderPaymentAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68F896412D5E4321000B308B /* POSCollectOrderPaymentAnalytics.swift */; }; @@ -3259,6 +3260,7 @@ 011D7A342CEC87B60007C187 /* CardPresentModalErrorEmailSent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalErrorEmailSent.swift; sourceTree = ""; }; 011DF3432C53A5CF000AFDD9 /* PointOfSaleCardPresentPaymentValidatingOrderMessageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentValidatingOrderMessageViewModel.swift; sourceTree = ""; }; 011DF3452C53A919000AFDD9 /* PointOfSaleCardPresentPaymentActivityIndicatingMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentActivityIndicatingMessageView.swift; sourceTree = ""; }; + 0139BB512D91B45500C78FDE /* CouponRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CouponRowView.swift; sourceTree = ""; }; 013D2FB32CFEFEA800845D75 /* BuiltInCardReaderMerchantEducationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuiltInCardReaderMerchantEducationPresenter.swift; sourceTree = ""; }; 013D2FB52CFF54B600845D75 /* TapToPayEducationStepsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapToPayEducationStepsFactory.swift; sourceTree = ""; }; 014BD4B72C64E2BA0011A66E /* PointOfSaleOrderSyncErrorMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleOrderSyncErrorMessageView.swift; sourceTree = ""; }; @@ -4808,7 +4810,7 @@ 68E952CF287587BF0095A23D /* CardReaderManualRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardReaderManualRowView.swift; sourceTree = ""; }; 68E952D12875A44B0095A23D /* CardReaderType+Manual.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CardReaderType+Manual.swift"; sourceTree = ""; }; 68ED2BD52ADD2C8C00ECA88D /* LineDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineDetailView.swift; sourceTree = ""; }; - 68F151E02C0DA7910082AEC8 /* CartItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartItem.swift; sourceTree = ""; }; + 68F151E02C0DA7910082AEC8 /* Cart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cart.swift; sourceTree = ""; }; 68F68A4F2D6730DF00BB9568 /* POSCollectOrderPaymentAnalyticsTracking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSCollectOrderPaymentAnalyticsTracking.swift; sourceTree = ""; }; 68F68A512D67365900BB9568 /* MockPOSCollectOrderPaymentAnalyticsTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPOSCollectOrderPaymentAnalyticsTracker.swift; sourceTree = ""; }; 68F896412D5E4321000B308B /* POSCollectOrderPaymentAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSCollectOrderPaymentAnalytics.swift; sourceTree = ""; }; @@ -7242,6 +7244,7 @@ 68C53CBD2C1FE59B00C6D80B /* ItemListView.swift */, 026826A32BF59DF60036F959 /* CartView.swift */, 026826A22BF59DF60036F959 /* ItemRowView.swift */, + 0139BB512D91B45500C78FDE /* CouponRowView.swift */, 68D8FBD02BFEF9C700477C42 /* TotalsView.swift */, 68AF3C3A2D01481A006F1ED2 /* POSReceiptEligibilityBanner.swift */, DA1D68C12C36F0980097859A /* PointOfSaleAssets.swift */, @@ -9929,7 +9932,7 @@ 20F7B12E2D12CBE700C08193 /* ItemsViewState.swift */, 20C909952D3151FA0013BCCF /* ItemListBaseItem.swift */, 20D4AE002D133B43004555B2 /* ItemsStackState.swift */, - 68F151E02C0DA7910082AEC8 /* CartItem.swift */, + 68F151E02C0DA7910082AEC8 /* Cart.swift */, 20D920E92CEF86520023B089 /* PointOfSaleErrorState.swift */, 2044158C2CE4DB480070BF54 /* PointOfSaleOrderStage.swift */, 2044158E2CE6181E0070BF54 /* PointOfSaleOrderState.swift */, @@ -16081,6 +16084,7 @@ E1E636BB26FB467A00C9D0D7 /* Comparable+Woo.swift in Sources */, CE315DC42CC91A4A00A06748 /* WooShippingServiceViewModel.swift in Sources */, 450C2CB024CF006A00D570DD /* ProductTagsDataSource.swift in Sources */, + 0139BB522D91B45800C78FDE /* CouponRowView.swift in Sources */, DEB3879E2C34FE620025256E /* GoogleAdsCampaignCoordinator.swift in Sources */, EE45E2BA2A409BA40085F227 /* TooltipPresenter.swift in Sources */, 023D69BC2589BF5900F7DA72 /* PrintShippingLabelCoordinator.swift in Sources */, @@ -16578,7 +16582,7 @@ 57C5FF7F250925C90074EC26 /* OrderListViewModel.swift in Sources */, 029D02602C231F5F00CB1E75 /* PointOfSaleCardPresentPaymentReaderUpdateCompletionView.swift in Sources */, 26E0AE13263359F900A5EB3B /* View+Conditionals.swift in Sources */, - 68F151E12C0DA7910082AEC8 /* CartItem.swift in Sources */, + 68F151E12C0DA7910082AEC8 /* Cart.swift in Sources */, CE583A072107849F00D73C1C /* SwitchTableViewCell.swift in Sources */, EE7E75A82D83EB1F00E6FF5B /* WooShippingSplitShipmentsRow.swift in Sources */, D8149F562251EE300006A245 /* UITextField+Helpers.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleOrderControllerTests.swift b/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleOrderControllerTests.swift index a61e34738d2..798def4dfd3 100644 --- a/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleOrderControllerTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleOrderControllerTests.swift @@ -20,7 +20,7 @@ struct PointOfSaleOrderControllerTests { receiptService: mockReceiptService) // When - await sut.syncOrder(for: [], retryHandler: {}) + await sut.syncOrder(for: .init(), retryHandler: {}) // Then #expect(mockOrderService.syncOrderWasCalled == false) @@ -35,12 +35,12 @@ struct PointOfSaleOrderControllerTests { let fakeOrder = Order.fake().copy(items: [orderItem]) let cartItem = makeItem(orderItemsToMatch: [orderItem]) mockOrderService.orderToReturn = fakeOrder - await sut.syncOrder(for: [cartItem], retryHandler: {}) + await sut.syncOrder(for: .init(items: [cartItem]), retryHandler: {}) mockOrderService.syncOrderWasCalled = false // When - await sut.syncOrder(for: [cartItem], retryHandler: {}) + await sut.syncOrder(for: .init(items: [cartItem]), retryHandler: {}) // Then #expect(mockOrderService.syncOrderWasCalled == false) @@ -53,14 +53,14 @@ struct PointOfSaleOrderControllerTests { receiptService: mockReceiptService) mockOrderService.simulateSyncing = true Task { - await sut.syncOrder(for: [makeItem(quantity: 1)], retryHandler: {}) + await sut.syncOrder(for: .init(items: [makeItem(quantity: 1)]), retryHandler: {}) } try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC))) mockOrderService.syncOrderWasCalled = false // When - await sut.syncOrder(for: [makeItem(quantity: 2), - makeItem(quantity: 5)], + await sut.syncOrder(for: .init(items: [makeItem(quantity: 2), + makeItem(quantity: 5)]), retryHandler: {}) // Then @@ -80,7 +80,7 @@ struct PointOfSaleOrderControllerTests { currencySettings: currencySettings) // When - await sut.syncOrder(for: [makeItem()], retryHandler: {}) + await sut.syncOrder(for: .init(items: [makeItem()]), retryHandler: {}) // Then #expect(mockOrderService.spySyncOrderCurrency == .AUD) @@ -99,8 +99,8 @@ struct PointOfSaleOrderControllerTests { let futureOrderItem = OrderItem.fake().copy(quantity: 5) // When - await sut.syncOrder(for: [cartItem, - makeItem(quantity: 5, orderItemsToMatch: [futureOrderItem])], + await sut.syncOrder(for: .init(items: [cartItem, + makeItem(quantity: 5, orderItemsToMatch: [futureOrderItem])]), retryHandler: {}) // Then @@ -131,7 +131,7 @@ struct PointOfSaleOrderControllerTests { observeOrderState() // When - await sut.syncOrder(for: [makeItem()], retryHandler: {}) + await sut.syncOrder(for: .init(items: [makeItem()]), retryHandler: {}) } await orderStateAppendTask?.value @@ -169,7 +169,7 @@ struct PointOfSaleOrderControllerTests { observeOrderState() // When - await sut.syncOrder(for: [makeItem()], retryHandler: {}) + await sut.syncOrder(for: .init(items: [makeItem()]), retryHandler: {}) } await orderStateAppendTask?.value @@ -209,7 +209,7 @@ struct PointOfSaleOrderControllerTests { mockOrderService.orderToReturn = order // We need an existing order before we can update its email, and send a receipt: - await sut.syncOrder(for: [makeItem()], retryHandler: { }) + await sut.syncOrder(for: .init(items: [makeItem()]), retryHandler: { }) // When try await sut.sendReceipt(recipientEmail: recipientEmail) @@ -249,7 +249,7 @@ struct PointOfSaleOrderControllerTests { let orderItem = OrderItem.fake() let fakeOrder = Order.fake().copy(items: [orderItem]) mockOrderService.orderToReturn = fakeOrder - await sut.syncOrder(for: [makeItem()], retryHandler: {}) + await sut.syncOrder(for: .init(items: [makeItem()]), retryHandler: {}) // When let completionResult: Bool = await withCheckedContinuation { continuation in @@ -287,7 +287,7 @@ struct PointOfSaleOrderControllerTests { mockOrderService.orderToReturn = fakeOrder // When - let result = await sut.syncOrder(for: [fakeCartItem], retryHandler: { }) + let result = await sut.syncOrder(for: .init(items: [fakeCartItem]), retryHandler: { }) // Then if case .success(let state) = result { @@ -308,10 +308,10 @@ struct PointOfSaleOrderControllerTests { // When // 1. Initial order - _ = await sut.syncOrder(for: [makeItem()], retryHandler: {}) + _ = await sut.syncOrder(for: .init(items: [makeItem()]), retryHandler: {}) // 2. Sync existing order - let result = await sut.syncOrder(for: [makeItem(), makeItem()], retryHandler: {}) + let result = await sut.syncOrder(for: .init(items: [makeItem(), makeItem()]), retryHandler: {}) // Then if case .success(let state) = result { @@ -333,10 +333,10 @@ struct PointOfSaleOrderControllerTests { // When // 1. Initial order - _ = await sut.syncOrder(for: [cartItem], retryHandler: {}) + _ = await sut.syncOrder(for: .init(items: [cartItem]), retryHandler: {}) // 2. Syncing existing order with same cart should not update order - let result = await sut.syncOrder(for: [cartItem], retryHandler: {}) + let result = await sut.syncOrder(for: .init(items: [cartItem]), retryHandler: {}) // Then if case .success(let state) = result { @@ -354,7 +354,7 @@ struct PointOfSaleOrderControllerTests { // When mockOrderService.orderToReturn = nil - let result = await sut.syncOrder(for: [cartItem], retryHandler: {}) + let result = await sut.syncOrder(for: .init(items: [cartItem]), retryHandler: {}) // Then if case .failure(let error) = result { @@ -386,7 +386,7 @@ struct PointOfSaleOrderControllerTests { orderService.orderToReturn = fakeOrder // When - await sut.syncOrder(for: [fakeCartItem], retryHandler: { }) + await sut.syncOrder(for: .init(items: [fakeCartItem]), retryHandler: { }) // Then #expect(analyticsProvider.receivedEvents.first(where: { $0 == "order_creation_success" }) != nil) @@ -401,7 +401,7 @@ struct PointOfSaleOrderControllerTests { orderService.orderToReturn = nil // When - await sut.syncOrder(for: [makeItem()], retryHandler: {}) + await sut.syncOrder(for: .init(items: [makeItem()]), retryHandler: {}) // Then #expect(analyticsProvider.receivedEvents.first(where: { $0 == "order_creation_failed" }) != nil) @@ -429,7 +429,7 @@ struct PointOfSaleOrderControllerTests { let orderItem = OrderItem.fake() let fakeOrder = Order.fake().copy(items: [orderItem]) mockOrderService.orderToReturn = fakeOrder - await sut.syncOrder(for: [makeItem()], retryHandler: {}) + await sut.syncOrder(for: .init(items: [makeItem()]), retryHandler: {}) // When await withCheckedContinuation { continuation in diff --git a/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift index 3ec34e0191d..db032509607 100644 --- a/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift +++ b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift @@ -47,12 +47,14 @@ final class MockPointOfSaleAggregateModel: PointOfSaleAggregateModelProtocol { func loadNextItems(base: ItemListBaseItem) async { } - var cart: [CartItem] = [] + var cart: Cart = .init() func addToCart(_ item: POSItem) { } func remove(cartItem: CartItem) { } + func remove(cartCouponItem: CartCouponItem) { } + var removeAllItemsFromCartCalled = false func removeAllItemsFromCart() { removeAllItemsFromCartCalled = true diff --git a/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleOrderController.swift b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleOrderController.swift index 93a282d17fb..b6fb9ff29f8 100644 --- a/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleOrderController.swift +++ b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleOrderController.swift @@ -21,10 +21,10 @@ final class MockPointOfSaleOrderController: PointOfSaleOrderControllerProtocol { var syncOrderResultToReturn: Result = .success(.newOrder) @discardableResult - func syncOrder(for cartProducts: [CartItem], + func syncOrder(for cart: Cart, retryHandler: @escaping () async -> Void) async -> Result { syncOrderWasCalled = true - spyCartProducts = cartProducts + spyCartProducts = cart.items spyRetryHandler = retryHandler guard let orderStateToReturn else { diff --git a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift index 8e6962e44a3..971403d8758 100644 --- a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift @@ -15,6 +15,7 @@ struct PointOfSaleAggregateModelTests { let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) // Then #expect(sut.orderStage == .building) @@ -26,11 +27,12 @@ struct PointOfSaleAggregateModelTests { let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) sut.addToCart(makeItem()) await sut.checkOut() try #require(sut.orderStage == .finalizing) - try #require(sut.cart.isNotEmpty) + try #require(!sut.cart.isEmpty) // When sut.startNewCart() @@ -46,6 +48,7 @@ struct PointOfSaleAggregateModelTests { let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) sut.addToCart(makeItem()) @@ -62,6 +65,7 @@ struct PointOfSaleAggregateModelTests { let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) sut.addToCart(makeItem()) await sut.checkOut() @@ -100,7 +104,7 @@ struct PointOfSaleAggregateModelTests { sut.addToCart(item) // Then - #expect(sut.cart.isNotEmpty) + #expect(!sut.cart.isEmpty) } @available(iOS 17.0, *) @@ -117,7 +121,7 @@ struct PointOfSaleAggregateModelTests { items.forEach(sut.addToCart(_:)) // Then - #expect(sut.cart.map(\.item.id) == items.reversed().map(\.id)) + #expect(sut.cart.items.map(\.item.id) == items.reversed().map(\.id)) } @available(iOS 17.0, *) @@ -133,15 +137,15 @@ struct PointOfSaleAggregateModelTests { sut.addToCart(item) sut.addToCart(anotherItem) - try #require(sut.cart.count == 2) + try #require(sut.cart.items.count == 2) // When - let firstItem = try #require(sut.cart.first) + let firstItem = try #require(sut.cart.items.first) sut.remove(cartItem: firstItem) // Then - #expect(sut.cart.count == 1) - #expect(sut.cart.first?.title == "Item 1") + #expect(sut.cart.items.count == 1) + #expect(sut.cart.items.first?.title == "Item 1") } @available(iOS 17.0, *) @@ -157,7 +161,7 @@ struct PointOfSaleAggregateModelTests { sut.addToCart(item) sut.addToCart(anotherItem) - try #require(sut.cart.count == 2) + try #require(sut.cart.items.count == 2) // When sut.removeAllItemsFromCart() @@ -207,6 +211,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) sut.addToCart(makeItem()) @@ -226,11 +231,12 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) sut.addToCart(makeItem()) sut.addToCart(makeItem()) - let item = try #require(sut.cart.first) + let item = try #require(sut.cart.items.first) // When await sut.checkOut() @@ -248,6 +254,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) sut.addToCart(makeItem()) @@ -269,6 +276,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) sut.addToCart(makeItem()) @@ -290,6 +298,7 @@ struct PointOfSaleAggregateModelTests { let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) // When @@ -309,6 +318,7 @@ struct PointOfSaleAggregateModelTests { let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) do { @@ -330,6 +340,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) sut.addToCart(makeItem()) @@ -356,6 +367,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) // Then @@ -388,6 +400,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) cardPresentPaymentService.paymentEvent = .show(eventDetails: .paymentSuccess(done: {})) @@ -426,6 +439,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) cardPresentPaymentService.paymentEvent = .show( @@ -449,6 +463,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) // When @@ -467,6 +482,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) // When @@ -484,6 +500,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) await sut.startCashPayment() #expect(sut.paymentState == .cash(.collectingCash)) @@ -503,6 +520,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) #expect(sut.orderStage == .building) @@ -532,6 +550,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) // When @@ -553,6 +572,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) struct TestError: Error {} @@ -580,6 +600,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) struct TestError: Error {} await sut.checkOut() @@ -610,6 +631,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) cardPresentPaymentService.connectedReader = nil @@ -639,6 +661,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) cardPresentPaymentService.connectedReader = .init(name: "Test reader", batteryLevel: 0.7) orderController.orderStateToReturn = makeLoadedOrderState(orderTotal: "$0.01", orderTotalDecimal: 0.01) @@ -658,6 +681,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) cardPresentPaymentService.connectedReader = .init(name: "Test reader", batteryLevel: 0.7) orderController.orderStateToReturn = makeLoadedOrderState(orderTotal: "$0.00", orderTotalDecimal: 0.0) @@ -677,6 +701,7 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) cardPresentPaymentService.connectedReader = CardPresentPaymentCardReader(name: "Test", batteryLevel: 0.5) @@ -707,9 +732,10 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) orderController.orderStateToReturn = makeLoadedOrderState(cartTotal: "$1.00") - await orderController.syncOrder(for: [], retryHandler: {}) + await orderController.syncOrder(for: .init(), retryHandler: {}) struct TestError: Error {} cardPresentPaymentService.onCancelPaymentCalled = { @@ -732,8 +758,12 @@ struct PointOfSaleAggregateModelTests { itemsController: itemsController, cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, + analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker()) - let onboardingViewModel = CardPresentPaymentsOnboardingViewModel(fixedState: .pluginNotActivated(plugin: .stripe)) + let onboardingViewModel = CardPresentPaymentsOnboardingViewModel( + fixedState: .pluginNotActivated(plugin: .stripe), + useCase: MockCardPresentPaymentsOnboardingUseCase(initial: .pluginNotActivated(plugin: .stripe)) + ) cardPresentPaymentService.paymentEvent = .idle try #require(sut.cardPresentPaymentOnboardingViewModel == nil) @@ -769,7 +799,10 @@ struct PointOfSaleAggregateModelTests { sut.addToCart(makeItem()) - let onboardingViewModel = CardPresentPaymentsOnboardingViewModel(fixedState: .noConnectionError) + let onboardingViewModel = CardPresentPaymentsOnboardingViewModel( + fixedState: .noConnectionError, + useCase: MockCardPresentPaymentsOnboardingUseCase(initial: .noConnectionError) + ) cardPresentPaymentService.paymentEvent = .showOnboarding(onboardingViewModel: onboardingViewModel, onCancel: {}) // When diff --git a/WooCommerce/WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift b/WooCommerce/WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift index 63120b9fbf7..f41cdaa5415 100644 --- a/WooCommerce/WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift +++ b/WooCommerce/WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift @@ -74,16 +74,16 @@ struct CartViewHelperTests { } @Test func shouldShowClearCartButton_empty_cart_false() async throws { - #expect(sut.shouldShowClearCartButton(cart: [], orderStage: .building) == false) - #expect(sut.shouldShowClearCartButton(cart: [], orderStage: .finalizing) == false) + #expect(sut.shouldShowClearCartButton(cart: .init(), orderStage: .building) == false) + #expect(sut.shouldShowClearCartButton(cart: .init(), orderStage: .finalizing) == false) } @Test func shouldShowClearCartButton_items_in_cart_and_building_true() async throws { - #expect(sut.shouldShowClearCartButton(cart: [makeItem()], orderStage: .building) == true) + #expect(sut.shouldShowClearCartButton(cart: .init(items: [makeItem()]), orderStage: .building) == true) } @Test func shouldShowClearCartButton_items_in_cart_and_finalizing_false() async throws { - #expect(sut.shouldShowClearCartButton(cart: [makeItem()], orderStage: .finalizing) == false) + #expect(sut.shouldShowClearCartButton(cart: .init(items: [makeItem()]), orderStage: .finalizing) == false) } } diff --git a/Yosemite/Yosemite/PointOfSale/POSCoupon.swift b/Yosemite/Yosemite/PointOfSale/POSCoupon.swift index f66255829e6..1abdfcc90b0 100644 --- a/Yosemite/Yosemite/PointOfSale/POSCoupon.swift +++ b/Yosemite/Yosemite/PointOfSale/POSCoupon.swift @@ -1,4 +1,9 @@ public struct POSCoupon: Equatable, Hashable { public let id: UUID - public let couponID: Int64 + public let code: String + + public init(id: UUID, code: String) { + self.id = id + self.code = code + } } diff --git a/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift b/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift index da8bb81a0c1..4b9e13a6ec1 100644 --- a/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift +++ b/Yosemite/Yosemite/PointOfSale/PointOfSaleItemService.swift @@ -125,7 +125,7 @@ public final class PointOfSaleItemService: PointOfSaleItemServiceProtocol { private func mapCouponsToPOSItems(coupons: [Coupon]) -> [POSItem] { coupons.compactMap { coupon in - .coupon(POSCoupon(id: UUID(), couponID: coupon.couponID)) + .coupon(POSCoupon(id: UUID(), code: coupon.code)) } }