is_callable( $coupon, 'get_meta' ) ) { $product_quantity_restrictions = $coupon->get_meta( 'wc_sc_product_quantity_restrictions' ); } else { $product_quantity_restrictions = $this->get_post_meta( $coupon_id, 'wc_sc_product_quantity_restrictions', true ); } if ( ! is_array( $product_quantity_restrictions ) ) { $product_quantity_restrictions = array(); } $cart_min_quantity = ! empty( $product_quantity_restrictions['values']['cart']['min'] ) ? intval( $product_quantity_restrictions['values']['cart']['min'] ) : ''; $cart_max_quantity = ! empty( $product_quantity_restrictions['values']['cart']['max'] ) ? intval( $product_quantity_restrictions['values']['cart']['max'] ) : ''; $product_quantity_restrictions_type = ! empty( $product_quantity_restrictions['type'] ) ? $product_quantity_restrictions['type'] : 'cart'; ?>

$value ) { if ( 0 !== $product_id ) { $product = wc_get_product( $product_id ); if ( ! empty( $product ) && is_object( $product ) ) { $product_name = is_callable( array( $product, 'get_name' ) ) ? $product->get_name() : ''; $product_max_quantity = ! empty( $value['max'] ) ? intval( $value['max'] ) : ''; $product_min_quantity = ! empty( $value['min'] ) ? intval( $value['min'] ) : ''; ?>

$value ) { if ( 0 !== $category_id ) { $term = get_term_by( 'id', $category_id, 'product_cat', ARRAY_A ); if ( ! empty( $term ) && is_array( $term ) ) { $category_name = ! empty( $term['name'] ) ? $term['name'] : ''; ?>

$restrictions ) { if ( 'values' === $restriction_key ) { // Max quantity feature not included for product quantity. foreach ( $restrictions['product'] as $id => $restriction ) { $id = absint( $id ); if ( 0 !== $id && isset( $product_quantity_restrictions['values']['product'][ $id ]['max'] ) ) { $product_quantity_restrictions['values']['product'][ $id ]['max'] = ! empty( $restriction['max'] ) ? intval( $restriction['max'] ) : ''; } } // Max quantity feature not included for product category quantity. foreach ( $restrictions['product_category'] as $id => $restriction ) { $id = absint( $id ); if ( 0 !== $id && isset( $product_quantity_restrictions['values']['product_category'][ $id ]['max'] ) ) { $product_quantity_restrictions['values']['product_category'][ $id ]['max'] = ! empty( $restriction['max'] ) ? intval( $restriction['max'] ) : ''; } } } } } if ( $this->is_callable( $coupon, 'update_meta_data' ) && $this->is_callable( $coupon, 'save' ) ) { $coupon->update_meta_data( 'wc_sc_product_quantity_restrictions', $product_quantity_restrictions ); $coupon->save(); } else { $this->update_post_meta( $post_id, 'wc_sc_product_quantity_restrictions', $product_quantity_restrictions ); } } /** * Validate the coupon based on product quantity * * @param boolean $valid Is valid or not. * @param WC_Coupon $coupon The coupon object. * @param WC_Discounts $wc_discounts The discounts object. * * @return boolean Is valid or not * @throws Exception If the coupon is invalid. */ public function validate( $valid = false, $coupon = null, $wc_discounts = null ) { // If coupon is invalid already, no need for further checks. if ( false === $valid ) { return $valid; } if ( ! is_a( $coupon, 'WC_Coupon' ) ) { return $valid; } if ( ! is_a( $wc_discounts, 'WC_Discounts' ) ) { return $valid; } $items_to_validate = array(); $cart_quantity = 0; if ( is_callable( array( $wc_discounts, 'get_items_to_validate' ) ) ) { $items_to_validate = $wc_discounts->get_items_to_validate(); } else { return $valid; } if ( ! empty( $items_to_validate ) ) { foreach ( $items_to_validate as $key => $cart_content ) { $cart_item = ! empty( $cart_content->object ) ? $cart_content->object : array(); $cart_quantity += ! empty( $cart_item['quantity'] ) ? intval( $cart_item['quantity'] ) : 0; } } else { return $valid; } // If the cart quantity is empty the rule will not work. if ( $cart_quantity <= 0 ) { return $valid; } $coupon_id = is_callable( array( $coupon, 'get_id' ) ) ? $coupon->get_id() : 0; if ( $this->is_callable( $coupon, 'get_meta' ) ) { $product_quantity_restrictions = $coupon->get_meta( 'wc_sc_product_quantity_restrictions' ); } else { $product_quantity_restrictions = $this->get_post_meta( $coupon_id, 'wc_sc_product_quantity_restrictions', true ); } if ( is_array( $product_quantity_restrictions ) && ! empty( $product_quantity_restrictions ) ) { $type = ! empty( $product_quantity_restrictions['type'] ) ? $product_quantity_restrictions['type'] : ''; $values = ! empty( $product_quantity_restrictions['values'] ) ? $product_quantity_restrictions['values'] : ''; $condition = ! empty( $product_quantity_restrictions['condition'] ) ? $product_quantity_restrictions['condition'] : 'any'; switch ( $type ) { case 'cart': $min = ! empty( $product_quantity_restrictions['values']['cart']['min'] ) ? intval( $product_quantity_restrictions['values']['cart']['min'] ) : 0; $max = ! empty( $product_quantity_restrictions['values']['cart']['max'] ) ? intval( $product_quantity_restrictions['values']['cart']['max'] ) : 0; $messages = array( __( 'Your cart does not meet the quantity requirement.', 'woocommerce-smart-coupons' ), ); if ( empty( $min ) && empty( $max ) ) { return $valid; } elseif ( empty( $min ) && ! empty( $max ) && $cart_quantity <= $max ) { return $valid; } elseif ( empty( $max ) && ! empty( $min ) && $cart_quantity >= $min ) { return $valid; } elseif ( ! empty( $min ) && ! empty( $max ) && $cart_quantity >= $min && $cart_quantity <= $max ) { return $valid; } else { if ( $cart_quantity > $max ) { /* translators: 1. Number of quantity 2. Singular or plural text based on number of quantities */ $messages[] = sprintf( __( 'Your cart should have a maximum of %1$d %2$s in total.', 'woocommerce-smart-coupons' ), $max, _n( 'quantity', 'quantities', $max ) ); } if ( $cart_quantity < $min ) { /* translators: 1. Number of quantity 2. Singular or plural text based on number of quantities */ $messages[] = sprintf( __( 'Your cart should have a minimum of %1$d %2$s in total.', 'woocommerce-smart-coupons' ), $min, _n( 'quantity', 'quantities', $min ) ); } throw new Exception( implode( ' ', $messages ) ); } break; default: case 'product': $product_quantity_restrictions = ! empty( $values['product'] ) ? $values['product'] : array(); $product_category_quantity_restrictions = ! empty( $values['product_category'] ) ? $values['product_category'] : array(); $params = array( 'condition' => $condition, 'items_to_validate' => $items_to_validate, ); $product_condition = $this->process_product_quantities( $product_quantity_restrictions, $params ); $product_category_condition = $this->process_category_quantities( $product_category_quantity_restrictions, $params ); if ( false === $product_condition && false === $product_category_condition ) { throw new Exception( __( 'Your cart does not meet the product quantity requirement.', 'woocommerce-smart-coupons' ) ); } elseif ( 'empty' === $product_condition && false === $product_category_condition ) { throw new Exception( __( 'Your cart does not meet the product quantity requirement.', 'woocommerce-smart-coupons' ) ); } elseif ( false === $product_condition && 'empty' === $product_category_condition ) { throw new Exception( __( 'Your cart does not meet the product quantity requirement.', 'woocommerce-smart-coupons' ) ); } break; } } return $valid; } /** * Make meta data of this plugin, protected * * @param bool $protected Is protected. * @param string $meta_key the meta key. * @param string $meta_type The meta type. * @return bool $protected */ public function make_sc_meta_protected( $protected, $meta_key, $meta_type ) { if ( 'wc_sc_product_quantity_restrictions' === $meta_key ) { return true; } return $protected; } /** * Add product quantity restriction meta with value in coupon meta * * @param array $data The row data. * @param array $post The POST values. * @return array Modified data */ public function generate_coupon_meta( $data = array(), $post = array() ) { if ( isset( $post['wc_sc_product_quantity_restrictions'] ) && is_array( $post['wc_sc_product_quantity_restrictions'] ) ) { $data['wc_sc_product_quantity_restrictions'] = $this->product_quantity_restrictions_encode( $post['wc_sc_product_quantity_restrictions'] ); } return $data; } /** * Post meta defaults for product quantity restriction meta * * @param array $defaults Existing postmeta defaults. * @return array */ public function postmeta_defaults( $defaults = array() ) { return array_merge( $defaults, array( 'wc_sc_product_quantity_restrictions' => '' ) ); } /** * Function to handle coupon meta data during export of existing coupons * * @param mixed $meta_value The meta value. * @param array $args Additional arguments. * @return string Processed meta value */ public function export_coupon_meta( $meta_value = '', $args = array() ) { $index = ( ! empty( $args['index'] ) ) ? $args['index'] : -1; $meta_keys = ( ! empty( $args['meta_keys'] ) ) ? $args['meta_keys'] : array(); $meta_values = ( ! empty( $args['meta_values'] ) ) ? $args['meta_values'] : array(); if ( $index >= 0 && ! empty( $meta_keys[ $index ] ) && 'wc_sc_product_quantity_restrictions' === $meta_keys[ $index ] && ! empty( $meta_values[ $index ] ) ) { $meta_value = maybe_unserialize( $meta_values[ $index ] ); // phpcs:ignore $meta_value = $this->product_quantity_restrictions_encode( $meta_value ); } return $meta_value; } /** * Process coupon meta value for import * * @param mixed $meta_value The meta value. * @param array $args Additional Arguments. * @return mixed $meta_value */ public function process_coupon_meta_value_for_import( $meta_value = null, $args = array() ) { if ( ! empty( $args['meta_key'] ) && 'wc_sc_product_quantity_restrictions' === $args['meta_key'] && ! empty( $args['postmeta']['wc_sc_product_quantity_restrictions'] ) ) { $meta_value = $this->product_quantity_restrictions_decode( $args['postmeta']['wc_sc_product_quantity_restrictions'] ); } return $meta_value; } /** * Add product quantity restriction meta in export headers * * @param array $headers Existing headers. * @return array */ public function export_headers( $headers = array() ) { $product_quantity_restriction_headers = array( 'wc_sc_product_quantity_restrictions' => __( 'Product quantity based restrictions', 'woocommerce-smart-coupons' ), ); return array_merge( $headers, $product_quantity_restriction_headers ); } /** * Function to copy coupon product quantity restriction meta in newly generated coupon * * @param array $args The arguments. */ public function copy_meta( $args = array() ) { $new_coupon_id = ( ! empty( $args['new_coupon_id'] ) ) ? absint( $args['new_coupon_id'] ) : 0; $coupon = ( ! empty( $args['ref_coupon'] ) ) ? $args['ref_coupon'] : false; if ( empty( $new_coupon_id ) || empty( $coupon ) ) { return; } $add_product_details = array(); if ( $this->is_wc_gte_30() && $this->is_callable( $coupon, 'get_meta' ) && $this->is_callable( $coupon, 'update_meta_data' ) && $this->is_callable( $coupon, 'save' ) ) { $add_product_details = $coupon->get_meta( 'wc_sc_product_quantity_restrictions' ); $coupon->update_meta_data( 'wc_sc_product_quantity_restrictions', $add_product_details ); $coupon->save(); } else { $old_coupon_id = ( ! empty( $coupon->id ) ) ? $coupon->id : 0; $add_product_details = $this->get_post_meta( $old_coupon_id, 'wc_sc_product_quantity_restrictions', true ); $this->update_post_meta( $new_coupon_id, 'wc_sc_product_quantity_restrictions', $add_product_details ); } $new_coupon = new WC_Coupon( $new_coupon_id ); if ( $this->is_callable( $new_coupon, 'update_meta_data' ) && $this->is_callable( $new_coupon, 'save' ) ) { $new_coupon->update_meta_data( 'wc_sc_product_quantity_restrictions', $add_product_details ); $new_coupon->save(); } else { update_post_meta( $new_coupon_id, 'wc_sc_product_quantity_restrictions', $add_product_details ); } } /** * Process cart product quantities * * @param array $product_quantity_restrictions values. * @param array $params condition and cart contents. * @return bool * @throws Exception If empty product quantities. */ public function process_product_quantities( $product_quantity_restrictions = array(), $params = array() ) { if ( ! empty( $product_quantity_restrictions ) ) { $status = array(); $condition = ! empty( $params['condition'] ) ? $params['condition'] : 'any'; $items_to_validate = ! empty( $params['items_to_validate'] ) ? $params['items_to_validate'] : array(); foreach ( $product_quantity_restrictions as $id => $restriction ) { if ( 0 === $id ) { continue; } $min_quantity = ! empty( $restriction['min'] ) ? intval( $restriction['min'] ) : 0; $max_quantity = ! empty( $restriction['max'] ) ? intval( $restriction['max'] ) : 0; $cart_product_quantities = $this->cart_product_quantities( $items_to_validate ); $product_quantity = ! empty( $cart_product_quantities[ $id ] ) ? intval( $cart_product_quantities[ $id ] ) : 1; if ( empty( $min_quantity ) && empty( $max_quantity ) ) { $status[] = 'empty'; } elseif ( empty( $min_quantity ) && ! empty( $max_quantity ) && $product_quantity <= $max_quantity ) { $status[] = 'true'; } elseif ( empty( $max_quantity ) && ! empty( $min_quantity ) && $product_quantity >= $min_quantity ) { $status[] = 'true'; } elseif ( ! empty( $min_quantity ) && ! empty( $max_quantity ) && $product_quantity >= $min_quantity && $product_quantity <= $max_quantity ) { $status[] = 'true'; } else { $status[] = 'false'; } } switch ( $condition ) { case 'all': if ( in_array( 'false', $status, true ) ) { return false; } elseif ( in_array( 'true', $status, true ) ) { return true; } else { return 'empty'; } default: case 'any': if ( in_array( 'true', $status, true ) ) { return true; } elseif ( in_array( 'false', $status, true ) ) { return false; } else { return 'empty'; } } } return 'empty'; } /** * Process cart category quantities * * @param array $product_category_quantity_restrictions values. * @param array $params condition and cart contents. * @return bool * @throws Exception If empty cart category quantities. */ public function process_category_quantities( $product_category_quantity_restrictions = array(), $params = array() ) { if ( ! empty( $product_category_quantity_restrictions ) ) { $status = array(); $condition = ! empty( $params['condition'] ) ? $params['condition'] : 'any'; $items_to_validate = ! empty( $params['items_to_validate'] ) ? $params['items_to_validate'] : array(); $cart_product_categories_quantities = $this->cart_product_categories_quantities( $items_to_validate ); foreach ( $product_category_quantity_restrictions as $id => $restriction ) { $min_quantity = ! empty( $restriction['min'] ) ? intval( $restriction['min'] ) : ''; $max_quantity = ! empty( $restriction['max'] ) ? intval( $restriction['max'] ) : ''; if ( 0 === $id ) { continue; } $category_quantity = ! empty( $cart_product_categories_quantities[ $id ] ) ? intval( $cart_product_categories_quantities[ $id ] ) : 0; if ( empty( $min_quantity ) && empty( $max_quantity ) ) { $status[] = 'empty'; } elseif ( empty( $min_quantity ) && ! empty( $max_quantity ) && $category_quantity <= $max_quantity ) { $status[] = 'true'; } elseif ( empty( $max_quantity ) && ! empty( $min_quantity ) && $category_quantity >= $min_quantity ) { $status[] = 'true'; } elseif ( ! empty( $min_quantity ) && ! empty( $max_quantity ) && $category_quantity >= $min_quantity && $category_quantity <= $max_quantity ) { $status[] = 'true'; } else { $status[] = 'false'; } } switch ( $condition ) { case 'all': if ( in_array( 'false', $status, true ) ) { return false; } elseif ( in_array( 'true', $status, true ) ) { return true; } else { return 'empty'; } default: case 'any': if ( in_array( 'true', $status, true ) ) { return true; } elseif ( in_array( 'false', $status, true ) ) { return false; } else { return 'empty'; } } } return 'empty'; } /** * Calculate product quantities * * @param array $cart_contents cart contents. * @return array */ public function cart_product_quantities( $cart_contents = array() ) { if ( empty( $cart_contents ) ) { return $cart_contents; } $cart_product_quantities = array(); foreach ( $cart_contents as $cart_content ) { $cart_item = ! empty( $cart_content->object ) ? $cart_content->object : array(); $quantity = ! empty( $cart_item['quantity'] ) ? intval( $cart_item['quantity'] ) : 1; $product_id = ! empty( $cart_item['product_id'] ) ? intval( $cart_item['product_id'] ) : 0; $variation_id = ! empty( $cart_item['variation_id'] ) ? intval( $cart_item['variation_id'] ) : 0; if ( empty( $cart_content->object['wc_sc_product_source'] ) ) { if ( ! empty( $variation_id ) ) { if ( isset( $cart_product_quantities[ $variation_id ] ) ) { $cart_product_quantities[ $variation_id ] = $cart_product_quantities[ $variation_id ] + $quantity; } else { $cart_product_quantities[ $variation_id ] = $quantity; } } if ( isset( $cart_product_quantities[ $product_id ] ) ) { $cart_product_quantities[ $product_id ] = $cart_product_quantities[ $product_id ] + $quantity; } else { $cart_product_quantities[ $product_id ] = $quantity; } } } return $cart_product_quantities; } /** * Calculate category quantities * * @param array $cart_contents cart contents. * @return array */ public function cart_product_categories_quantities( $cart_contents = array() ) { if ( empty( $cart_contents ) ) { return $cart_contents; } $categories_quantities = array(); foreach ( $cart_contents as $key => $cart_content ) { $cart_item = ! empty( $cart_content->object ) ? $cart_content->object : array(); $product = ! empty( $cart_item['data'] ) ? $cart_item['data'] : array(); $quantity = ! empty( $cart_item['quantity'] ) ? intval( $cart_item['quantity'] ) : 1; if ( is_object( $product ) && is_callable( array( $product, 'get_category_ids' ) ) ) { $product_variation = ( is_callable( array( $product, 'is_type' ) ) ) ? $product->is_type( 'variation' ) : false; if ( $product_variation ) { $parent_id = ( is_callable( array( $product, 'get_parent_id' ) ) ) ? $product->get_parent_id() : 0; if ( ! empty( $parent_id ) ) { $product = wc_get_product( $parent_id ); } } $categories = $product->get_category_ids(); if ( ! empty( $categories ) ) { foreach ( $categories as $category ) { if ( isset( $categories_quantities[ $category ] ) ) { $categories_quantities[ $category ] = $categories_quantities[ $category ] + $quantity; } else { $categories_quantities[ $category ] = $quantity; } } } } } return $categories_quantities; } /** * Calculate category quantities * * @param array $input_array . * @return string */ public function product_quantity_restrictions_encode( $input_array = array() ) { if ( ! is_array( $input_array ) || empty( $input_array ) || ! isset( $input_array['type'] ) ) { return ''; } $encode_string = ''; switch ( $input_array['type'] ) { case 'cart': if ( isset( $input_array['values'] ) && isset( $input_array['values']['cart'] ) && ! empty( $input_array['values']['cart'] ) ) { $cart_data = $input_array['values']['cart']; $min = isset( $cart_data['min'] ) && ! empty( $cart_data['min'] ) && is_scalar( $cart_data['min'] ) ? (int) $cart_data['min'] : 0; $max = isset( $cart_data['max'] ) && ! empty( $cart_data['max'] ) && is_scalar( $cart_data['max'] ) ? (int) $cart_data['max'] : 0; $encode_string = 'cart@' . $min . '_' . $max; } break; case 'product': $segment = array(); if ( isset( $input_array['values'] ) && isset( $input_array['values']['product'] ) && ! empty( $input_array['values']['product'] ) ) { $item_product = array(); foreach ( $input_array['values']['product'] as $product_id => $product_data ) { $product_id = (int) $product_id; $min = isset( $product_data['min'] ) && ! empty( $product_data['min'] ) && is_scalar( $product_data['min'] ) ? (int) $product_data['min'] : 0; $max = isset( $product_data['max'] ) && ! empty( $product_data['max'] ) && is_scalar( $product_data['max'] ) ? (int) $product_data['max'] : 0; if ( ! empty( $product_id ) ) { $item_product[] = $product_id . '-' . $min . '_' . $max; } } if ( ! empty( $item_product ) ) { $segment[] = 'product@' . implode( '#', $item_product ); } } if ( isset( $input_array['values'] ) && isset( $input_array['values']['product_category'] ) && ! empty( $input_array['values']['product_category'] ) ) { $item_category = array(); foreach ( $input_array['values']['product_category'] as $category_id => $category_data ) { $category_id = (int) $category_id; $min = isset( $category_data['min'] ) && ! empty( $category_data['min'] ) && is_scalar( $category_data['min'] ) ? (int) $category_data['min'] : 0; $max = isset( $category_data['max'] ) && ! empty( $category_data['max'] ) && is_scalar( $category_data['max'] ) ? (int) $category_data['max'] : 0; if ( ! empty( $category_id ) ) { $item_category[] = $category_id . '-' . $min . '_' . $max; } } if ( ! empty( $item_category ) ) { $segment[] = 'category@' . implode( '#', $item_category ); } } if ( ! empty( $segment ) ) { $encode_string = implode( '|', $segment ); } break; } return $encode_string; } /** * Calculate category quantities * * @param string $decode_string . * @return array */ public function product_quantity_restrictions_decode( $decode_string = '' ) { if ( empty( $decode_string ) || ! is_string( $decode_string ) ) { return array(); } $input_array = array( 'type' => '', 'values' => array(), 'condition' => 'any', ); $segments = explode( '|', $decode_string ); if ( is_array( $segments ) && ! empty( $segments ) ) { foreach ( $segments as $segment ) { $parts = explode( '@', $segment ); switch ( $parts[0] ) { case 'cart': $cart_values = explode( '_', $parts[1] ); $input_array['type'] = 'cart'; $input_array['values']['cart'] = array( 'min' => (int) ( $cart_values[0] ?? 0 ), 'max' => (int) ( $cart_values[1] ?? 0 ), ); break; case 'product': $product_values = explode( '#', $parts[1] ); foreach ( $product_values as $product_item ) { $product_data = explode( '-', $product_item ); $input_array['type'] = 'product'; $min_max = isset( $product_data[1] ) && ! empty( $product_data[1] ) ? explode( '_', $product_data[1] ) : array(); $input_array['values']['product'][ $product_data[0] ] = array( 'min' => (int) ( $min_max[0] ?? 0 ), 'max' => (int) ( $min_max[1] ?? 0 ), ); } break; case 'category': $category_values = explode( '#', $parts[1] ); foreach ( $category_values as $category_item ) { $category_data = explode( '-', $category_item ); $input_array['type'] = 'product'; $min_max = isset( $category_data[1] ) && ! empty( $category_data[1] ) ? explode( '_', $category_data[1] ) : array(); $input_array['values']['product_category'][ $category_data[0] ] = array( 'min' => (int) ( $min_max[0] ?? 0 ), 'max' => (int) ( $min_max[1] ?? 0 ), ); } break; } } } return $input_array; } } } WC_SC_Coupons_By_Product_Quantity::get_instance();