Files
wc-tier-and-package-prices/includes/class-wc-tpp-cart.php
magdev d55ada7924 Release version 1.1.3 - Cart quantity visibility fix
Fixed persistent cart quantity input visibility issues for products with
package quantity restrictions. Enhanced implementation ensures quantity
inputs are properly hidden in both main cart and mini-cart/sidebar.

Fixes:
- Cart quantity inputs now properly hidden with increased filter priority
- Mini-cart quantity inputs correctly replaced with read-only text
- Added fallback CSS injection to handle theme/plugin conflicts
- Enhanced DOM targeting with data attributes and multiple CSS selectors

Technical Changes:
- Increased filter priority to 999 for woocommerce_cart_item_quantity
- Added woocommerce_widget_cart_item_quantity filter for mini-cart support
- Added add_cart_quantity_css() method for dynamic CSS injection
- Added maybe_hide_mini_cart_quantity_input() method
- Enhanced quantity spans with data-product-id attribute
- Added wc-tpp-restricted-qty CSS class
- Implemented sibling (+) and general sibling (~) CSS selectors

Updated Files:
- includes/class-wc-tpp-cart.php (enhanced with mini-cart support)
- wc-tier-and-package-prices.php (version 1.1.3)
- composer.json (version 1.1.3)
- CHANGELOG.md (v1.1.3 section)
- All translation files (.pot, .po, .mo) updated to version 1.1.3

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 19:19:18 +01:00

219 lines
9.4 KiB
PHP

<?php
/**
* Cart price calculation for tier and package pricing
*/
if (!defined('ABSPATH')) {
exit;
}
class WC_TPP_Cart {
public function __construct() {
add_action('woocommerce_before_calculate_totals', array($this, 'apply_tier_package_pricing'), 10, 1);
add_filter('woocommerce_cart_item_price', array($this, 'display_cart_item_price'), 10, 3);
add_filter('woocommerce_cart_item_subtotal', array($this, 'display_cart_item_subtotal'), 10, 3);
add_filter('woocommerce_add_to_cart_validation', array($this, 'validate_package_quantity'), 10, 3);
add_filter('woocommerce_cart_item_quantity', array($this, 'maybe_hide_cart_quantity_input'), 999, 3);
add_filter('woocommerce_widget_cart_item_quantity', array($this, 'maybe_hide_mini_cart_quantity_input'), 999, 3);
add_action('wp_head', array($this, 'add_cart_quantity_css'));
}
public function apply_tier_package_pricing($cart) {
if (is_admin() && !defined('DOING_AJAX')) {
return;
}
// Prevent infinite loops
if (did_action('woocommerce_before_calculate_totals') >= 2) {
return;
}
// Check if cart object is valid
if (!$cart || !is_a($cart, 'WC_Cart')) {
return;
}
foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
$product_id = $cart_item['product_id'];
$quantity = $cart_item['quantity'];
$product = $cart_item['data'];
// Validate product object
if (!$product || !is_a($product, 'WC_Product')) {
continue;
}
// Check for exact package match first
$package_price = null;
if (get_option('wc_tpp_enable_package_pricing') === 'yes') {
$package_price = WC_TPP_Frontend::get_package_price($product_id, $quantity);
}
if ($package_price !== null) {
// Apply package pricing (total price divided by quantity)
$unit_price = $package_price / $quantity;
$product->set_price($unit_price);
// Store pricing information in cart item for display
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_pricing_type'] = 'package';
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_total_price'] = $package_price;
} else {
// Apply tier pricing if no package match
if (get_option('wc_tpp_enable_tier_pricing') === 'yes') {
$tier_price = WC_TPP_Frontend::get_tier_price($product_id, $quantity);
if ($tier_price !== null) {
$product->set_price($tier_price);
// Store pricing information in cart item for display
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_pricing_type'] = 'tier';
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_unit_price'] = $tier_price;
}
}
}
}
}
public function display_cart_item_price($price, $cart_item, $cart_item_key) {
if (isset($cart_item['wc_tpp_pricing_type'])) {
if ($cart_item['wc_tpp_pricing_type'] === 'package') {
$total_price = isset($cart_item['wc_tpp_total_price']) ? $cart_item['wc_tpp_total_price'] : $cart_item['line_total'];
$unit_price = $total_price / $cart_item['quantity'];
return wc_price($unit_price) . ' <small class="wc-tpp-notice">(' . __('Package price', 'wc-tier-package-prices') . ')</small>';
} elseif ($cart_item['wc_tpp_pricing_type'] === 'tier') {
$unit_price = isset($cart_item['wc_tpp_unit_price']) ? $cart_item['wc_tpp_unit_price'] : $cart_item['data']->get_price();
return wc_price($unit_price) . ' <small class="wc-tpp-notice">(' . __('Volume discount', 'wc-tier-package-prices') . ')</small>';
}
}
return $price;
}
public function display_cart_item_subtotal($subtotal, $cart_item, $cart_item_key) {
if (isset($cart_item['wc_tpp_pricing_type']) && $cart_item['wc_tpp_pricing_type'] === 'package') {
$total_price = isset($cart_item['wc_tpp_total_price']) ? $cart_item['wc_tpp_total_price'] : $cart_item['line_total'];
return wc_price($total_price);
}
return $subtotal;
}
public function validate_package_quantity($passed, $product_id, $quantity) {
// Check if restriction is enabled globally or for this product
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
if (!$global_restrict && !$product_restrict) {
return $passed;
}
// Get packages for this product
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
if (empty($packages) || !is_array($packages)) {
return $passed;
}
// Check if the quantity matches any package
$valid_quantity = false;
$available_quantities = array();
foreach ($packages as $package) {
$available_quantities[] = $package['qty'];
if ($quantity == $package['qty']) {
$valid_quantity = true;
break;
}
}
if (!$valid_quantity) {
$product = wc_get_product($product_id);
$product_name = $product ? $product->get_name() : __('this product', 'wc-tier-package-prices');
wc_add_notice(
sprintf(
__('The quantity %1$d is not available for %2$s. Please choose from the available package sizes: %3$s', 'wc-tier-package-prices'),
$quantity,
$product_name,
implode(', ', $available_quantities)
),
'error'
);
return false;
}
return $passed;
}
public function maybe_hide_cart_quantity_input($product_quantity, $cart_item_key, $cart_item) {
$product_id = $cart_item['product_id'];
// Check if restriction is enabled globally or for this product
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
// Get packages for this product
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
// If restriction is enabled and packages exist, show quantity as text only
if (($global_restrict || $product_restrict) && !empty($packages)) {
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s</span>',
$product_id,
$cart_item['quantity']
);
}
return $product_quantity;
}
public function maybe_hide_mini_cart_quantity_input($product_quantity, $cart_item, $cart_item_key) {
$product_id = $cart_item['product_id'];
// Check if restriction is enabled globally or for this product
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
// Get packages for this product
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
// If restriction is enabled and packages exist, show quantity as text only
if (($global_restrict || $product_restrict) && !empty($packages)) {
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s &times;</span>',
$product_id,
$cart_item['quantity']
);
}
return $product_quantity;
}
public function add_cart_quantity_css() {
// Get all cart items and check which products have restrictions
if (!WC()->cart) {
return;
}
$restricted_products = array();
foreach (WC()->cart->get_cart() as $cart_item) {
$product_id = $cart_item['product_id'];
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
if (($global_restrict || $product_restrict) && !empty($packages)) {
$restricted_products[] = $product_id;
}
}
if (!empty($restricted_products)) {
echo '<style type="text/css">';
foreach ($restricted_products as $product_id) {
// Hide quantity inputs for restricted products in cart
echo '.cart_item .wc-tpp-restricted-qty[data-product-id="' . esc_attr($product_id) . '"] + .quantity,';
echo '.cart_item .wc-tpp-restricted-qty[data-product-id="' . esc_attr($product_id) . '"] ~ .quantity,';
echo '.woocommerce-mini-cart-item .wc-tpp-restricted-qty[data-product-id="' . esc_attr($product_id) . '"] + .quantity,';
echo '.woocommerce-mini-cart-item .wc-tpp-restricted-qty[data-product-id="' . esc_attr($product_id) . '"] ~ .quantity { display: none !important; }';
}
echo '</style>';
}
}
}
new WC_TPP_Cart();