fix: prevent POS price modifications from persisting to product database#509
fix: prevent POS price modifications from persisting to product database#509
Conversation
When order_item_product() clones a product and sets POS prices (price, sale_price, regular_price, tax_status), WC stock functions can later call save() on the clone, writing those temporary values to the DB. Fix uses two mechanisms: - apply_changes() clears the $changes array so get_props_to_update() won't include price fields from its normal tracking - update_post_metadata filter blocks the !metadata_exists() fallback that would still write _sale_price when no DB row exists The clone keeps its real ID so WC stock management works normally.
📝 WalkthroughWalkthroughIntroduces in-memory protection for POS-modified product price metadata in the Orders class. A new protect_product_price_meta method hooks into WordPress's update_post_metadata filter to prevent persistence of specific price-related meta keys for products tracked during order item processing. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@includes/Orders.php`:
- Around line 262-269: The protect_product_price_meta method currently has an
unused parameter $meta_value which triggers PHPMD; rename the parameter to
$_meta_value (or prefix with an underscore) in the function signature and all
internal references to silence the unused-parameter warning, keeping the
function name protect_product_price_meta and its behavior unchanged (return true
when $object_id is in $this->price_protected_ids and $meta_key in
self::PROTECTED_META_KEYS, otherwise return $check).
🧹 Nitpick comments (1)
includes/Orders.php (1)
28-48: Consider set-style tracking for protected IDs.Using an associative array avoids duplicates and makes membership checks O(1), which helps for large orders.
♻️ Proposed refactor (multi-location)
- /** - * `@var` array<int> - */ - private $price_protected_ids = array(); + /** + * `@var` array<int, bool> + */ + private $price_protected_ids = array();- if ( $product->get_id() ) { - $this->price_protected_ids[] = $product->get_id(); - } + if ( $product->get_id() ) { + $this->price_protected_ids[ $product->get_id() ] = true; + }- if ( \in_array( $object_id, $this->price_protected_ids, true ) + if ( isset( $this->price_protected_ids[ $object_id ] ) && \in_array( $meta_key, self::PROTECTED_META_KEYS, true ) ) {
| public function protect_product_price_meta( $check, $object_id, $meta_key, $meta_value ) { | ||
| if ( \in_array( $object_id, $this->price_protected_ids, true ) | ||
| && \in_array( $meta_key, self::PROTECTED_META_KEYS, true ) | ||
| ) { | ||
| return true; | ||
| } | ||
|
|
||
| return $check; |
There was a problem hiding this comment.
Silence the unused-parameter warning.
PHPMD flags $meta_value as unused; rename it to $_meta_value (or suppress) to keep the signal clean.
🧹 Proposed fix
-public function protect_product_price_meta( $check, $object_id, $meta_key, $meta_value ) {
+public function protect_product_price_meta( $check, $object_id, $meta_key, $_meta_value ) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public function protect_product_price_meta( $check, $object_id, $meta_key, $meta_value ) { | |
| if ( \in_array( $object_id, $this->price_protected_ids, true ) | |
| && \in_array( $meta_key, self::PROTECTED_META_KEYS, true ) | |
| ) { | |
| return true; | |
| } | |
| return $check; | |
| public function protect_product_price_meta( $check, $object_id, $meta_key, $_meta_value ) { | |
| if ( \in_array( $object_id, $this->price_protected_ids, true ) | |
| && \in_array( $meta_key, self::PROTECTED_META_KEYS, true ) | |
| ) { | |
| return true; | |
| } | |
| return $check; |
🧰 Tools
🪛 PHPMD (2.15.0)
[warning] 262-262: Avoid unused parameters such as '$meta_value'. (undefined)
(UnusedFormalParameter)
🤖 Prompt for AI Agents
In `@includes/Orders.php` around lines 262 - 269, The protect_product_price_meta
method currently has an unused parameter $meta_value which triggers PHPMD;
rename the parameter to $_meta_value (or prefix with an underscore) in the
function signature and all internal references to silence the unused-parameter
warning, keeping the function name protect_product_price_meta and its behavior
unchanged (return true when $object_id is in $this->price_protected_ids and
$meta_key in self::PROTECTED_META_KEYS, otherwise return $check).
PHPUnit Test Results704 tests 696 ✅ 1m 1s ⏱️ Results for commit 13842a1. |
Summary
order_item_product()clones a product and sets POS prices on it, WC stock functions (wc_reduce_stock_levels,wc_increase_stock_levels) can later callsave()on the clone, inadvertently writingsale_price,price,regular_price, andtax_statusto the DBapply_changes()to clear the$changestracking array, plus anupdate_post_metadatafilter to block the!metadata_exists()fallback in WC'sget_props_to_update()(which writes_sale_pricewhen no DB row exists, regardless of$changes)set_id(0)Test plan
_woocommerce_pos_datameta$item->get_product()) hassale_price=20.00andis_on_sale()=true— in-memory values work correctlysave()on the filtered product (simulating what WC stock functions do)sale_priceis still empty andpriceis still 25 — no price data leaked to the databaseis_on_sale()behavior forexclude_sale_itemsis preserved)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests