-
Notifications
You must be signed in to change notification settings - Fork 120
[Local catalog] Handle missing products during order creation #16353
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
base: feat/woomob-1104-local-catalog-analytics
Are you sure you want to change the base?
[Local catalog] Handle missing products during order creation #16353
Conversation
Only keep the one confirmed error code observed in production: - order_item_product_invalid_variation_id Removed unverified error codes that were assumed but not confirmed: - order_item_product_invalid_id - woocommerce_rest_invalid_product_id Additional error codes can be added as they are discovered through testing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
…ame product Previously, when multiple variations of the same product were in the cart and one was deleted on the web, clicking "Remove products" would remove ALL variations instead of just the deleted one. Root cause: The removal logic matched items by product name, which is the same for all variations of a product. Solution: - Added UUID field to MissingProductInfo struct to uniquely identify cart items - Updated POSCart comparison logic to preserve individual UUIDs for each cart item - Changed removeMissingProductsFromCart() to match by UUID instead of name - Server-side errors (no UUID available) still remove all items as fallback This ensures that only the specific variation that failed is removed from the cart, not all variations of the parent product. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
When products or variations are detected as missing (either client-side or server-side validation), they are now automatically removed from the local catalog database to prevent them from appearing in future sessions. Changes: - Added deleteProducts method to POSCatalogPersistenceService to remove products/variations from GRDB - Added deleteProductsFromCatalog method to POSCatalogSyncCoordinator protocol and implementation - Added removeMissingProductsFromCatalog to PointOfSaleAggregateModel that extracts product/variation IDs and calls catalog deletion - Updated error view to await catalog deletion before retrying order creation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Added tests for: - Cart-to-order comparison detecting missing products - UUID-based identification distinguishing variations - Catalog deletion removing products and variations - Mock improvements to return order with items Test Coverage: - POSOrderServiceTests: 4 new tests for missing product detection - POSCatalogPersistenceServiceTests: 5 new tests for deletion 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Changed missing product identification from UUID-based to product/variation ID-based matching. This fixes two issues: 1. Multiple variations of the same product were all removed when only one was missing (UUID matched each cart item individually) 2. Variation names were computed incorrectly (attributes repeated 3x) Changes: - Modified CartOrderComparison.MissingCartItem to use productID/variationID - Updated cart comparison to consolidate by product/variation ID - Added extractProductIDs helper to determine product vs variation - Changed MissingProductInfo to use productID/variationID instead of UUID - Updated error view to extract and use product/variation IDs - Updated aggregate model removal to match by product/variation ID - Fixed test filter syntax to use filter(sql:) pattern - Updated tests to verify productID/variationID instead of UUID 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
This update improves error handling when products or variations are unavailable during order creation: - Add `data` field to NetworkError to capture additional error details from the API - Extract variation_id from error data when available (direct API connections) - Look up variation names from cart to show specific product information - Conditionally show "Remove product" button only when specific products can be identified - Show "Edit order" button for all error cases - Pass cart through error handling chain to enable variation name lookup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Generated by 🚫 Danger |
There was a problem hiding this 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 adds error handling for missing products during order creation in the Point of Sale (POS) system. When products or variations are unavailable (e.g., due to deletion without a full catalog resync), the system now detects these issues during checkout and displays an error message prompting merchants to remove the problematic items.
Key Changes:
- Implemented cart-to-order comparison logic to detect missing products and variations
- Added error handling for both client-side validation (after order creation) and server-side validation errors
- Created UI error messages to inform merchants about missing products with an option to remove them from the cart
- Added functionality to delete missing products from the local catalog
Reviewed Changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
Modules/Sources/Yosemite/Tools/POS/POSOrderService.swift |
Added validation to compare cart items with created order, throws error when items are missing |
Modules/Sources/Yosemite/Tools/POS/POSCart.swift |
Implemented cart-order comparison logic with new public types for reporting discrepancies |
Modules/Sources/Yosemite/Tools/POS/POSCatalogPersistenceService.swift |
Added method to delete specific products and variations from the local database |
Modules/Sources/Yosemite/Tools/POS/POSCatalogSyncCoordinator.swift |
Added public method to coordinate deletion of products from the catalog |
Modules/Sources/PointOfSale/Controllers/PointOfSaleOrderController.swift |
Enhanced error handling to extract missing product info from various error types and track analytics |
Modules/Sources/PointOfSale/Models/PointOfSaleOrderState.swift |
Added new missingProducts error case with associated product information |
Modules/Sources/PointOfSale/Models/PointOfSaleAggregateModel.swift |
Implemented logic to remove missing products from both cart and local catalog |
Modules/Sources/PointOfSale/Presentation/TotalsView.swift |
Integrated missing products error message view into the UI |
Modules/Sources/NetworkingCore/Network/NetworkError.swift |
Extended to expose errorData field for extracting additional error context |
Modules/Sources/Yosemite/Model/Orders/OrderItem+POSMatching.swift |
Added helper method for matching order items with cart items |
Modules/Tests/YosemiteTests/Tools/POS/POSOrderServiceTests.swift |
Added comprehensive tests for missing product detection scenarios |
Modules/Tests/YosemiteTests/Tools/POS/POSCatalogPersistenceServiceTests.swift |
Added tests for product deletion functionality |
| Various mock files | Updated mocks to support new protocol methods |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Modules/Sources/Yosemite/Tools/POS/POSCatalogSyncCoordinator.swift
Outdated
Show resolved
Hide resolved
Modules/Sources/Yosemite/Model/Orders/OrderItem+POSMatching.swift
Outdated
Show resolved
Hide resolved
Modules/Sources/Yosemite/Tools/POS/POSCatalogPersistenceService.swift
Outdated
Show resolved
Hide resolved
|
|
This update removes unavailable products from the catalog immediately when they're detected during order creation, rather than waiting for user action: - Remove identified products from catalog automatically in `checkOut()` after detecting missing products error - Add `removeMissingProductsFromCart()` method to remove items from cart only - Add `removeIdentifiedMissingProductsFromCatalog()` to handle catalog cleanup - Update "Remove product" button to only remove from cart (catalog already cleaned) - Works for both "Edit order" and "Remove product" flows This ensures unavailable items are removed from the catalog regardless of which button the merchant chooses, preventing the same items from being added again. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
- Move deleteProductsFromCatalog out of private extension to make it properly public - Add documentation for CartOrderComparison and nested types - Remove unused OrderItem+POSMatching extension - Optimize batch deletion using filter-based deleteAll instead of individual fetches and deletes This improves code organization, documentation, and performance for catalog operations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
This method is no longer needed since we now automatically remove products from the catalog when they're detected as missing during checkout. The other periphery warnings are for public API properties that are intentionally kept for API completeness even though not currently used internally. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
These properties are part of the public cart-order comparison API and are intentionally kept for API completeness and future use, even though they're not currently used internally. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Split long DDLogWarn message across multiple lines to comply with 163 character limit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Modules/Sources/PointOfSale/Controllers/PointOfSaleOrderController.swift
Show resolved
Hide resolved
Remove descriptions from periphery:ignore comments to ensure they're properly recognized by the tool. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Move periphery:ignore comments from inline to the line before declarations as required by periphery tool. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Modules/Sources/PointOfSale/Controllers/PointOfSaleOrderController.swift
Outdated
Show resolved
Hide resolved
Add three new analytics events to track when users encounter deleted/unavailable items during checkout: - woo_pos_checkout_outdated_item_detected_screen_shown: Tracked when the error screen is shown - woo_pos_checkout_outdated_item_detected_edit_order_tapped: Tracked when "Edit order" button is tapped - woo_pos_checkout_outdated_item_detected_remove_tapped: Tracked when "Remove product" button is tapped All events include: - reason: "deleted" (hardcoded) - sync_strategy: "local_catalog" or "remote" (based on isLocalCatalogEligible) Changes: - Added stat name constants to WooAnalyticsStat.swift - Added events to TracksProvider decoration list for pos_ prefix - Added syncStrategy property key to WooAnalyticsEvent+PointOfSale.swift - Added three event factory functions to WooAnalyticsEvent+PointOfSale.swift - Added tracking calls in PointOfSaleOrderSyncMissingProductsErrorMessageView.swift 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Remove incorrect 'public extension' wrapper - the functions should be directly inside the PointOfSale enum, not in a separate extension. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Add public access modifier to struct, init, and body to allow usage from TotalsView. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Remove public access modifiers - the view should be internal like other error message views in the same module. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
The quantity property was never read anywhere in the code - it was only set when creating MissingProductInfo instances. Removed it to simplify the data structure. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Move catalog cleanup logic into handlePostSyncCleanup() method to make it clear this is a post-sync operation rather than part of the checkout flow itself. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1. Extract shared ID extraction logic into Array extension on MissingProductInfo 2. Rename handlePostSyncCleanup to removeMissingProductsFromCatalogAfterSync for clarity 3. Use the new helper in both the error view and aggregate model This eliminates code duplication and makes the purpose of the method more explicit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
|
@iamgabrielma Apologies that these are getting long. I can split it if you like, e.g. move analytics out, but it seems to make more sense to review and test as a single feature. You should have an invite to a store for testing the specific variations removal. |
|
👋 I've started to review this one but I'm not sure I'll be able to finish it EOD |

Merge after #16345
Description
This PR shows merchants issues where a product is no longer available when they check out.
This can happen when a product or variation hasn't been resynced (i.e. no full sync) since it was deleted. When we create an order with these products, we show an error and prompt the merchant to remove the product, blocking checkout until it's resolved.
We also remove the item from the local catalog, when we can identify it.
There are some difficulties identifying missing variations, as we get different responses for these. That means we have to treat them separately, and when we can't identify the variation we don't give details of the missing items.
If a product is missing, we get the order back missing that line item – so we just compare it to the cart to find missing products.
If a variation is missing, we get an error back. Currently, we don't get the
variation_idback in the error, and we don't have any line items to compare with.In a separate backend PR we'll add the
variation_idto the error, and backend folks are ensuring that'll be passed back through the proxy when using a WPCom connection too. We don't want to require a new version of Woo to use this, so older versions will fall back to a generic error and figuring out the broken product themselves. SeeUnknown variation missingvideo below.Test Steps
Repeat with variations... but be aware that you'll only see the generic errors. I can invite you to my test site to see it with the variation ID returned.
Analytics
Screenshots
Unknown variation missing
unknown.missing.variation.-.01.mp4
Known missing variations and products
known.missing.products.and.variations.-.01.mp4
RELEASE-NOTES.txtif necessary.