data['maximum_quantity'] && $pricingRule->data['maximum_quantity'] <= $quantity ) { return ''; } return $postfix; }, 10, 3 ); add_filter( 'tiered_pricing_table/price/pricing_rule', function ( PricingRule $pricingRule, $productId ) { $pricingRule->data['maximum_quantity'] = DataProvider::getMaximumQuantity( $productId ); $pricingRule->data['group_of_quantity'] = DataProvider::getGroupOfQuantity( $productId ); return $pricingRule; }, 1, 2 ); add_action( 'woocommerce_before_calculate_totals', function ( \WC_Cart $cart ) { foreach ( $cart->get_cart_contents() as $cartItemKey => $cartItem ) { if ( $cartItem['data'] instanceof WC_Product ) { $productId = ! empty( $cartItem['variation_id'] ) ? $cartItem['variation_id'] : $cartItem['product_id']; $pricingRule = PriceManager::getPricingRule( $productId ); $max = $pricingRule->data['maximum_quantity'] ?? null; $groupOf = $pricingRule->data['group_of_quantity'] ?? null; $cartItemQuantity = $this->getProductCartQuantity( $productId ); if ( $max ) { $max = max( $groupOf, $max ); if ( $this->getProductCartQuantity( $productId ) > $max ) { $cart->cart_contents[ $cartItemKey ]['quantity'] = $max; // translators: %1$s: item name, %2$s: minimum quantity wc_add_notice( sprintf( __( 'Maximum quantity for the %1$s is %2$d', 'tier-pricing-table' ), $cartItem['data']->get_name(), $max ), 'error' ); } } if ( $groupOf ) { if ( 0 !== $cartItemQuantity % $groupOf ) { // translators: %s: quantity step wc_add_notice( sprintf( __( 'Order quantity must be multiple of %s', 'tier-pricing-table' ), $groupOf ), 'error' ); $cart->cart_contents[ $cartItemKey ]['quantity'] = ceil( $cartItemQuantity / $groupOf ) * $groupOf; } } } } }, 10, 1 ); add_filter( 'woocommerce_quantity_input_args', function ( $args, $product ) { if ( $product instanceof WC_Product && TierPricingTablePlugin::isSimpleProductSupported( $product ) ) { $pricingRule = PriceManager::getPricingRule( $product->get_id() ); $max = $pricingRule->data['maximum_quantity'] ?? null; $groupOf = $pricingRule->data['group_of_quantity'] ?? null; if ( $max ) { // Subtract what is already in the cart ONLY for the classic product-page input, where // the field means "how many more can be added". The Store API reads this filter's // max_value as the ABSOLUTE cart maximum (QuantityLimits::get_add_to_cart_limits), so // subtracting there double-counts the cart and caps customers at half the real limit. if ( ! is_cart() && ! is_checkout() && ! WC()->is_rest_api_request() ) { $max = max( 1, $max - $this->getProductCartQuantity( $product->get_id() ) ); } $max = max( $groupOf, $max ); $args['max_value'] = $max; } if ( $groupOf ) { $args['step'] = $groupOf; $args['min_value'] = max( $args['min_value'] ?? 1, $groupOf ); $args['value'] = max( $args['value'] ?? 1, $groupOf ); } } return $args; }, 9999999, 2 ); add_filter( 'woocommerce_add_to_cart_validation', function ( $passed, $_productId, $qty, $variationId = null ) { $productId = $variationId ? $variationId : $_productId; // Do not show additional notices if there are already notices if ( ! $passed ) { return false; } $pricingRule = PriceManager::getPricingRule( $productId ); $max = $pricingRule->data['maximum_quantity'] ?? null; $groupOf = $pricingRule->data['group_of_quantity'] ?? null; if ( $max ) { $max = max( $groupOf, $max ); if ( $qty + $this->getProductCartQuantity( $_productId ) > $max ) { // translators: %s: maximum quantity wc_add_notice( sprintf( __( 'Maximum order quantity for the product is %s', 'tier-pricing-table' ), $max ), 'error' ); return false; } } if ( $groupOf ) { if ( 0 !== $qty % $groupOf ) { // translators: %s: quantity step wc_add_notice( sprintf( __( 'Order quantity must be multiple of %s', 'tier-pricing-table' ), $groupOf ), 'error' ); return false; } } return $passed; }, 10, 4 ); add_action( 'wp_head', function () { if ( is_product() ) { ?> get_id() ); $max = $pricingRule->data['maximum_quantity'] ? intval( $pricingRule->data['maximum_quantity'] ) : null; $groupOf = $pricingRule->data['group_of_quantity'] ? intval( $pricingRule->data['group_of_quantity'] ) : null; if ( $max ) { if ( $quantity > $max ) { // translators: %s: maximum quantity wc_add_notice( sprintf( __( 'Maximum order quantity for the product is %s', 'tier-pricing-table' ), $max ), 'error' ); return false; } } if ( $groupOf ) { if ( 0 !== $quantity % $groupOf ) { // translators: %s: quantity step wc_add_notice( sprintf( __( 'Order quantity must be multiple of %s', 'tier-pricing-table' ), $groupOf ), 'error' ); return false; } } return $passed; }, 10, 4 ); add_filter( 'woocommerce_available_variation', function ( $variation ) { $pricingRule = PriceManager::getPricingRule( $variation['variation_id'] ); $max = $pricingRule->data['maximum_quantity'] ?? null; $groupOf = $pricingRule->data['group_of_quantity'] ?? null; if ( $max ) { $max = max( 1, $max - $this->getProductCartQuantity( $variation['variation_id'], true ) ); if ( $groupOf ) { $max = max( $groupOf, $max ); } $variation['max_qty'] = $max; } if ( $groupOf ) { $variation['step'] = $groupOf; $variation['min_qty'] = max( $variation['min_qty'], $groupOf ); $variation['qty_value'] = max( $variation['min_qty'], $groupOf ); } return $variation; }, 999 ); } public function getProductCartQuantity( $productId, $isVariation = false ) { $qty = 0; if ( is_array( wc()->cart->cart_contents ) ) { foreach ( wc()->cart->cart_contents as $cart_content ) { $compare = $isVariation ? ( $cart_content['variation_id'] ?? false ) : $cart_content['product_id']; if ( $compare == $productId ) { $qty += $cart_content['quantity']; } } } return $qty; } }