Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Experiments/Experiments/DefaultFeatureFlagService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
return buildConfig == .localDeveloper || buildConfig == .alpha
case .productGlobalUniqueIdentifierSupport:
return true
case .sendReceiptsForPointOfSale:
return true
case .hideSitesInStorePicker:
return true
case .filterHistoryOnOrderAndProductLists:
Expand Down
4 changes: 0 additions & 4 deletions Experiments/Experiments/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,6 @@ public enum FeatureFlag: Int {
///
case productGlobalUniqueIdentifierSupport

/// Adds support for sending receipts after the payment for POS
///
case sendReceiptsForPointOfSale

/// Supports hiding sites from the store picker
///
case hideSitesInStorePicker
Expand Down
7 changes: 4 additions & 3 deletions WooCommerce/Classes/Analytics/TracksProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ private extension TracksProvider {
WooAnalyticsStat.pointOfSaleGetSupportTapped,
WooAnalyticsStat.pointOfSaleSimpleProductsExplanationDialogShown,
WooAnalyticsStat.pointOfSaleCreateNewOrderTapped,
WooAnalyticsStat.pointOfSaleEmailReceiptTapped,
WooAnalyticsStat.pointOfSaleEmailReceiptSendTapped,
WooAnalyticsStat.pointOfSaleReceiptEmailSendTapped,
WooAnalyticsStat.pointOfSalePaymentsOnboardingShown,
WooAnalyticsStat.pointOfSalePaymentsOnboardingDismissed,
WooAnalyticsStat.pointOfSaleCardReaderConnectionTapped,
Expand Down Expand Up @@ -171,7 +170,9 @@ private extension TracksProvider {
WooAnalyticsStat.cardPresentOnboardingCtaFailed,

// Receipts
// TODO: 15058-gh
WooAnalyticsStat.receiptEmailTapped,
WooAnalyticsStat.receiptEmailSuccess,
WooAnalyticsStat.receiptEmailFailed,

// Payments
WooAnalyticsStat.collectPaymentCanceled,
Expand Down
3 changes: 1 addition & 2 deletions WooCommerce/Classes/Analytics/WooAnalyticsStat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1283,8 +1283,7 @@ enum WooAnalyticsStat: String {
case pointOfSaleGetSupportTapped = "get_support_tapped"
case pointOfSaleSimpleProductsExplanationDialogShown = "simple_products_explanation_dialog_shown"
case pointOfSaleCreateNewOrderTapped = "create_new_order_tapped"
case pointOfSaleEmailReceiptTapped = "email_receipt_tapped"
case pointOfSaleEmailReceiptSendTapped = "email_receipt_send_tapped"
case pointOfSaleReceiptEmailSendTapped = "receipt_email_send_tapped"
case pointOfSalePaymentsOnboardingShown = "payments_onboarding_shown"
case pointOfSalePaymentsOnboardingDismissed = "payments_onboarding_dismissed"
case pointOfSaleCardReaderConnectionTapped = "card_reader_connection_tapped"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,14 @@ extension PointOfSaleAggregateModel {

@MainActor
func sendReceipt(to emailAddress: String) async throws {
try await orderController.sendReceipt(recipientEmail: emailAddress)
do {
try await orderController.sendReceipt(recipientEmail: emailAddress)
analytics.track(.receiptEmailSuccess)
} catch {
// Catch and re-throw in order to capture the error event, but still allow the UI to handle the error state.
analytics.track(.receiptEmailFailed)
throw error
}
}

@MainActor
Expand Down
7 changes: 1 addition & 6 deletions WooCommerce/Classes/POS/Presentation/PaymentButtons.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,11 @@ struct PaymentsActionButtons: View {

private let receiptEligibilityUseCase = ReceiptEligibilityUseCase()

private var shouldShowSendReceiptButton: Bool {
ServiceLocator.featureFlagService.isFeatureFlagEnabled(.sendReceiptsForPointOfSale)
}

var body: some View {
ZStack {
VStack {
newOrderButton
sendReceiptButton
.renderedIf(shouldShowSendReceiptButton)
}
}
}
Expand All @@ -28,7 +23,7 @@ private extension PaymentsActionButtons {
var sendReceiptButton: some View {
Button(action: {
Task { @MainActor in
ServiceLocator.analytics.track(.pointOfSaleEmailReceiptTapped)
ServiceLocator.analytics.track(.receiptEmailTapped)
await handleSendReceiptAction()
}
}, label: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ struct POSSendReceiptView: View {
}

private func sendReceipt() {
ServiceLocator.analytics.track(.pointOfSaleEmailReceiptSendTapped)
ServiceLocator.analytics.track(.pointOfSaleReceiptEmailSendTapped)
Task { @MainActor in
guard isEmailValid else {
errorMessage = Localization.emailValidationErrorText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@ final class ReceiptEligibilityUseCase: ReceiptEligibilityUseCaseProtocol {
/// WooCommerce 9.5 allows to attach a customer email after payment is made and send email receipt via the API.
///
func isEligibleForPointOfSaleReceipts(onCompletion: @escaping (Bool) -> Void) {
guard featureFlagService.isFeatureFlagEnabled(.sendReceiptsForPointOfSale) else {
onCompletion(false)
return
}

Task { @MainActor in
let isWooCommerceSupported = await isPluginSupported(Constants.wcPluginName,
minimumVersion: Constants.PointOfSaleReceipts.wcPluginMinimumVersion)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ final class MockFeatureFlagService: FeatureFlagService {
var viewEditCustomFieldsInProductsAndOrders: Bool
var favoriteProducts: Bool
var isProductGlobalUniqueIdentifierSupported: Bool
var receiptsForPOS: Bool
var hideSitesInStorePicker: Bool

init(isInboxOn: Bool = false,
Expand All @@ -45,7 +44,6 @@ final class MockFeatureFlagService: FeatureFlagService {
viewEditCustomFieldsInProductsAndOrders: Bool = false,
favoriteProducts: Bool = false,
isProductGlobalUniqueIdentifierSupported: Bool = false,
receiptsForPOS: Bool = false,
hideSitesInStorePicker: Bool = false) {
self.isInboxOn = isInboxOn
self.isShowInboxCTAEnabled = isShowInboxCTAEnabled
Expand All @@ -67,7 +65,6 @@ final class MockFeatureFlagService: FeatureFlagService {
self.viewEditCustomFieldsInProductsAndOrders = viewEditCustomFieldsInProductsAndOrders
self.favoriteProducts = favoriteProducts
self.isProductGlobalUniqueIdentifierSupported = isProductGlobalUniqueIdentifierSupported
self.receiptsForPOS = receiptsForPOS
self.hideSitesInStorePicker = hideSitesInStorePicker
}

Expand Down Expand Up @@ -113,8 +110,6 @@ final class MockFeatureFlagService: FeatureFlagService {
return favoriteProducts
case .productGlobalUniqueIdentifierSupport:
return isProductGlobalUniqueIdentifierSupported
case .sendReceiptsForPointOfSale:
return receiptsForPOS
case .hideSitesInStorePicker:
return hideSitesInStorePicker
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,12 @@ final class MockPointOfSaleOrderController: PointOfSaleOrderControllerProtocol {
clearOrderWasCalled = true
}

func sendReceipt(recipientEmail: String) async { }
var shouldThrowReceiptError: Bool = false
var sendReceiptWasCalled: Bool = false
func sendReceipt(recipientEmail: String) async throws {
sendReceiptWasCalled = true
if shouldThrowReceiptError {
throw NSError(domain: "some error", code: -1)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,45 @@ struct PointOfSaleAggregateModelTests {
// Then
#expect(cardPresentPaymentService.collectPaymentChannel == .pos)
}

@available(iOS 17.0, *)
@Test func sendReceipt_when_invoked_then_calls_controller() async throws {
// Given
let orderController = MockPointOfSaleOrderController()
let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(),
cardPresentPaymentService: MockCardPresentPaymentService(),
orderController: orderController,
collectOrderPaymentAnalyticsTracker: MockCollectOrderPaymentAnalyticsTracker())

// When
try await sut.sendReceipt(to: "")

// Then
#expect(orderController.sendReceiptWasCalled == true)
}

@available(iOS 17.0, *)
@Test func sendReceipt_when_invoked_with_error_then_returns_error() async throws {
// Given
let orderController = MockPointOfSaleOrderController()
orderController.shouldThrowReceiptError = true
let expectedError = NSError(domain: "some error", code: -1)

let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(),
cardPresentPaymentService: MockCardPresentPaymentService(),
orderController: orderController,
collectOrderPaymentAnalyticsTracker: MockCollectOrderPaymentAnalyticsTracker())

do {
// When
try await sut.sendReceipt(to: "")
} catch {
// Then
let nsError = error as NSError
#expect(nsError.domain == expectedError.domain)
#expect(nsError.code == expectedError.code)
}
}
}

struct PaymentTests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,64 +88,6 @@ final class ReceiptEligibilityUseCaseTests: XCTestCase {
XCTAssertFalse(isEligible)
}

func test_isEligibleForPOSReceipts_when_WooCommerce_version_is_correct_version_then_returns_true() {
// Given
let featureFlag = MockFeatureFlagService(receiptsForPOS: true)
let stores = MockStoresManager(sessionManager: .makeForTesting())
let plugin = SystemPlugin.fake().copy(name: "WooCommerce",
version: "9.5",
active: true)

stores.whenReceivingAction(ofType: SystemStatusAction.self) { action in
switch action {
case let .fetchSystemPlugin(_, _, onCompletion):
onCompletion(plugin)
default:
XCTFail("Unexpected action")
}
}
let sut = ReceiptEligibilityUseCase(stores: stores, featureFlagService: featureFlag)

// When
let isEligible: Bool = waitFor { promise in
sut.isEligibleForPointOfSaleReceipts(onCompletion: { result in
promise(result)
})
}

// Then
XCTAssertTrue(isEligible)
}

func test_isEligibleForPOSReceipts_when_WooCommerce_version_is_incorrect_version_then_returns_false() {
// Given
let featureFlag = MockFeatureFlagService(receiptsForPOS: true)
let stores = MockStoresManager(sessionManager: .makeForTesting())
let plugin = SystemPlugin.fake().copy(name: "WooCommerce",
version: "9.4",
active: true)

stores.whenReceivingAction(ofType: SystemStatusAction.self) { action in
switch action {
case let .fetchSystemPlugin(_, _, onCompletion):
onCompletion(plugin)
default:
XCTFail("Unexpected action")
}
}
let sut = ReceiptEligibilityUseCase(stores: stores, featureFlagService: featureFlag)

// When
let isEligible: Bool = waitFor { promise in
sut.isEligibleForPointOfSaleReceipts(onCompletion: { result in
promise(result)
})
}

// Then
XCTAssertFalse(isEligible)
}

func test_when_WooCommerce_version_is_equal_or_above_minimum_then_returns_true() {
// Given
let stores = MockStoresManager(sessionManager: .makeForTesting())
Expand Down
Loading