Skip to content

[Woo POS] Make cash payment events independent from card payment events #15769

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: trunk
Choose a base branch
from

Conversation

staskus
Copy link
Contributor

@staskus staskus commented Jun 17, 2025

WOOMOB-120

Description

Keeping paymentState enum in a single variable can introduce unwanted issues. For example, having .collectCash state active when a later card payment event arrives and changes a view state.

The solution is to switch from enum'to struct` when managing a payment state. It allows changing card and cash states without affecting the overall payment state by mistake:

  • Define PointOfSalePaymentState as struct
  • Introduce activePaymentMethod variable that could be used for clarity within views
  • Access card or cash payment state when needed through paymentState.cash or paymentState.card
  • Update usage in helpers and views with as much clarity as possible, so a new payment method would trigger clear compiler errors where a new logic is necessary

Steps to reproduce

These changes shouldn't change the behavior. Verify:

  • Cash payment
  • Navigation between totals and cash payment
  • Having the card reader connected or disconnected

Testing information

Tested on:

  • iPad Air 18.4 Simulator
  • iPad Air 18.3 Device

Screenshots

cash.payment.events.MP4

  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

staskus added 7 commits June 17, 2025 21:22
Turn cash and card relationship from OR (enum) to AND (struct), allowing them to co-exist together.

Keeping both cash and card payment state in a single variable can cause occasional issues when card payment event unexpectedly arrives and changes the state while cash payment was in progress.
@staskus staskus added this to the 22.7 milestone Jun 17, 2025
@staskus staskus requested a review from Copilot June 17, 2025 19:26
@staskus staskus added type: task An internally driven task. feature: POS labels Jun 17, 2025
@dangermattic
Copy link
Collaborator

dangermattic commented Jun 17, 2025

1 Warning
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.

Generated by 🚫 Danger

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors PointOfSalePaymentState from an enum into a struct with separate card and cash states, adds an activePaymentMethod helper, and updates all dependent helpers, views, models, mocks, and tests accordingly.

  • Replace PointOfSalePaymentState enum with a struct containing card and cash properties
  • Introduce activePaymentMethod and route logic in helpers/views through it
  • Update aggregate model, mocks, and tests to initialize and compare the new struct state

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
WooCommerceTests/POS/ViewHelpers/TotalsViewHelperTests.swift Updated tests to wrap card state in new struct initializer
WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift Updated tests to initialize PointOfSalePaymentState(card:…)
WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift Adjusted expected state comparisons to new struct with card and cash
WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift Changed mock default paymentState to use empty PointOfSalePaymentState()
Classes/POS/ViewHelpers/TotalsViewHelper.swift Switched helper logic to use activePaymentMethod and separate .card and .cash branches
Classes/POS/ViewHelpers/CartViewHelper.swift Updated guard to reference paymentState.card and moved allowsCartEditing into PointOfSaleCardPaymentState
Classes/POS/Presentation/TotalsView.swift Rewrote UI switches to branch on activePaymentMethod and nested card/cash states
Classes/POS/Presentation/PointOfSaleDashboardView.swift Updated background environment binding to check .card state only
Classes/POS/Presentation/POSFloatingControlView.swift Changed disabling to use paymentState.card
Classes/POS/Models/PointOfSalePaymentState.swift Converted enum to struct, added activePaymentMethod, moved initializers
Classes/POS/Models/PointOfSaleAggregateModel.swift Adjusted default initializer, state mutations for card vs. cash events
Comments suppressed due to low confidence (2)

WooCommerce/WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift:12

  • Add tests for shouldPreventCartEditing when paymentState.cash is .collectingCash and .paymentSuccess to ensure cart editing is correctly prevented for cash payment scenarios.
        #expect(sut.shouldPreventCartEditing(orderState: .syncing,

WooCommerce/Classes/POS/ViewHelpers/TotalsViewHelper.swift:4

  • Consider adding tests for cash payment states to shouldShowTotalsFields, verifying it returns false for cash states like .collectingCash and .paymentSuccess.
    func shouldShowTotalsFields(for paymentState: PointOfSalePaymentState) -> Bool {

@wpmobilebot
Copy link
Collaborator

App Icon📲 You can test the changes from this Pull Request in WooCommerce iOS Prototype by scanning the QR code below to install the corresponding build.

App NameWooCommerce iOS Prototype
Build Number30523
VersionPR #15769
Bundle IDcom.automattic.alpha.woocommerce
Commit7ba5dab
Installation URL0tffm2vsod458
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@staskus staskus requested a review from iamgabrielma June 17, 2025 20:25
@staskus staskus marked this pull request as ready for review June 17, 2025 20:25
@iamgabrielma iamgabrielma self-assigned this Jun 20, 2025
Copy link
Contributor

@iamgabrielma iamgabrielma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looking good! I left a couple of non-blocking comments.

Allow me a couple of hours to test this on a card reader before approving, I have a bad internet today and updating the firmware is taking forever! Edit: Done!

@@ -177,7 +177,7 @@ extension PointOfSaleAggregateModel {

private func setStateForEditing() {
orderStage = .building
paymentState = .card(.idle)
paymentState = PointOfSalePaymentState()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question:

Should we remove the .idle states as default params when we initialize a PointOfSalePaymentState instance? While it makes sense in this function, being able to instantiate it without having to explicitly pass any initial values could make us mistakenly override current payment state if we call it in the wrong place, and reset the payment state to .idle when it shouldn't.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily for this PR but I wonder if there's a benefit of making TotalsViewHelper just hold static functions rather than instance functions, it's currently stateless and has no dependencies, we're not mocking anything specific, and doesn't need to conform to anything. (same case for CartViewHelper)

The question comes from seeing it initialized in each test, rather than the actual number of real allocations when the app runs, so it seems trivial either way, but happy to hear you thoughts in general about these 👍

@@ -80,7 +80,7 @@ struct PointOfSaleDashboardView: View {
.environment(\.floatingControlAreaSize,
CGSizeMake(floatingSize.width + Constants.floatingControlHorizontalOffset,
floatingSize.height + Constants.floatingControlVerticalOffset))
.environment(\.posBackgroundAppearance, posModel.paymentState != .card(.processingPayment) ? .primary : .secondary)
.environment(\.posBackgroundAppearance, posModel.paymentState.card != .processingPayment ? .primary : .secondary)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're already updating this let's extract the bg color:

private var backgroundAppearance: POSBackgroundAppearanceKey.Appearance {
        posModel.paymentState.card != .processingPayment ? .primary : .secondary
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature: POS type: task An internally driven task.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants