diff --git a/js/price/ppom-price.js b/js/price/ppom-price.js index 2c6319ed..52bf004d 100644 --- a/js/price/ppom-price.js +++ b/js/price/ppom-price.js @@ -1022,17 +1022,49 @@ function ppom_update_get_prices() { const apply_as_discount = ppom_pricematrix_discount == 'on' ? true : false; if ( ppom_pricematrix !== undefined ) { - jQuery.each( JSON.parse( ppom_pricematrix ), function ( range, meta ) { + const matrixData = JSON.parse(ppom_pricematrix); + const product_qty = parseInt(ppom_get_order_quantity(), 10); + + // Pre-process keys ONCE (fix performance + ordering issue) + const parsedKeys = Object.keys(matrixData).map((range) => { + if (range.includes('-')) { + const [start, end] = range.split('-').map(Number); + return { type: 'range', start, end, key: range }; + } else { + return { type: 'single', value: Number(range), key: range }; + } + }); + + parsedKeys.sort((a, b) => { + const aVal = a.type === 'range' ? a.start : a.value; + const bVal = b.type === 'range' ? b.start : b.value; + return aVal - bVal; + }); + + const firstKey = parsedKeys[0]?.key; + const lastKey = parsedKeys[parsedKeys.length - 1]?.key; + + jQuery.each(matrixData, function (range, meta) { const option_price = {}; + let isMatch = false; + + if (range.indexOf('-') !== -1) { + const [range_from, range_to] = range.split('-').map(Number); - const range_break = range.split( '-' ); - const range_from = parseInt( range_break[ 0 ] ); - const range_to = parseInt( range_break[ 1 ] ); - const product_qty = ppom_get_order_quantity(); + if (product_qty >= range_from && product_qty <= range_to) { + isMatch = true; + } + } else { + const value = Number(range); - // console.log(range, meta); + if (range === firstKey && product_qty <= value) { + isMatch = true; + } else if (range === lastKey && product_qty >= value) { + isMatch = true; + } + } - if ( product_qty >= range_from && product_qty <= range_to ) { + if (isMatch) { option_price.label = meta.label; option_price.price = meta.price; option_price.percent = meta.percent; @@ -1041,11 +1073,13 @@ function ppom_update_get_prices() { ? 'matrix_discount' : 'matrix'; option_price.data_name = ppom_pricematrix_id; - option_price.matrix_fixed = - meta.matrix_fixed == 'on' ? true : false; - options_price_added.push( option_price ); + option_price.matrix_fixed = meta.matrix_fixed === 'on'; + + options_price_added.push(option_price); + + return false; } - } ); + }); } // Variation quantities diff --git a/src/Support/Helpers.php b/src/Support/Helpers.php index 7ccf3f00..a2e1992a 100644 --- a/src/Support/Helpers.php +++ b/src/Support/Helpers.php @@ -1514,16 +1514,25 @@ public static function extract_matrix_by_quantity( $quantities_field, $product, return $matrix; } + $quantity = intval( $quantity ); foreach ( $ranges as $range => $data ) { - $range_array = explode( '-', $range ); - $range_start = $range_array[0]; - $range_end = $range_array[1]; + if ( strpos( $range, '-' ) !== false ) { + list( $start, $end ) = array_map( 'intval', explode( '-', $range ) ); + if ( $quantity >= $start && $quantity <= $end ) { + $matrix = $data; + break; + } + } else { + $value = intval( $range ); - $quantity = intval( $quantity ); - if ( $quantity >= $range_start && $quantity <= $range_end ) { - $matrix = $data; - break; + if ( $range === array_key_first( $ranges ) && $quantity <= $value ) { + $matrix = $data; + break; + } elseif ( $range === array_key_last( $ranges ) && $quantity >= $value ) { + $matrix = $data; + break; + } } } diff --git a/src/Validation/Validator.php b/src/Validation/Validator.php index c03800fc..4b41ff52 100644 --- a/src/Validation/Validator.php +++ b/src/Validation/Validator.php @@ -308,6 +308,11 @@ public static function get_product_limits( $product_id, $variation_id ) { $first_range = reset( $ranges ); $qty_ranges = explode( '-', $first_range['raw'] ); $min_quantity = $qty_ranges[0]; + + // If first options is not in range format, set min quantity to 1. + if ( ! isset( $qty_ranges[1] ) ) { + $min_quantity = 1; + } } } @@ -329,7 +334,7 @@ public static function get_product_limits( $product_id, $variation_id ) { $last_range = end( $ranges ); $qty_ranges = explode( '-', $last_range['raw'] ); - $max_quantity = $qty_ranges[1]; + $max_quantity = isset( $qty_ranges[1] ) ? $qty_ranges[1] : -1; } } diff --git a/tests/unit/test-checkout-lifecycle.php b/tests/unit/test-checkout-lifecycle.php index 110bf589..f4faaaae 100644 --- a/tests/unit/test-checkout-lifecycle.php +++ b/tests/unit/test-checkout-lifecycle.php @@ -262,13 +262,19 @@ public function testWooCommerceGetCartItemFromSessionRecalculatesMatrixPriceWhen 'price_matrix', array( array( - 'option' => '1-2', + 'option' => '5', 'price' => '12', 'label' => 'Low quantity', 'id' => 'low_qty', ), array( - 'option' => '3-5', + 'option' => '5-10', + 'price' => '10', + 'label' => 'Range quantity', + 'id' => 'range_qty', + ), + array( + 'option' => '11', 'price' => '8', 'label' => 'High quantity', 'id' => 'high_qty', @@ -279,11 +285,13 @@ public function testWooCommerceGetCartItemFromSessionRecalculatesMatrixPriceWhen $product->get_id() ); - $initial = $this->restore_cart_item_from_session( wc_get_product( $product->get_id() ), array(), 1 ); - $updated = $this->restore_cart_item_from_session( wc_get_product( $product->get_id() ), array(), 3 ); + $low_quantity = $this->restore_cart_item_from_session( wc_get_product( $product->get_id() ), array(), 3 ); + $range_quantity = $this->restore_cart_item_from_session( wc_get_product( $product->get_id() ), array(), 7 ); + $high_quantity = $this->restore_cart_item_from_session( wc_get_product( $product->get_id() ), array(), 15 ); - $this->assertSame( 12.0, (float) $initial['data']->get_price() ); - $this->assertSame( 8.0, (float) $updated['data']->get_price() ); + $this->assertSame( 12.0, (float) $low_quantity['data']->get_price() ); + $this->assertSame( 10.0, (float) $range_quantity['data']->get_price() ); + $this->assertSame( 8.0, (float) $high_quantity['data']->get_price() ); } /**