function init_woocommerce_product_restrictions()
{
    if (!class_exists('WooCommerce')) {
        return;
    }
    class WC_Product_Restrictions
    {
        private $version = '2.0';
        private $db_version = '1';
        private $admin;
        public $default_message;
        private $multiple_ofs_attributes = array();
        private $multiple_ofs_categories = array();
        private $product_variation_quantities = array();
        private $product_category_quantities = array();
        private $cart_must_be_multiple_of = 0;
        private $can_checkout = true;
        private $restrictions_loaded = false;
        /**
         * @var WC_Product_Restrictions The single instance of the class.
         * @since 2.0
         */
        protected static $_instance = null;
        public static function instance()
        {
            if (is_null(self::$_instance)) {
                self::$_instance = new self();
            }
            return self::$_instance;
        }
        /**
         * Constructor
         */
        public function __construct()
        {
            if (is_admin()) {
                require_once 'includes/WC_Product_Restrictions_Admin.php';
                $this->admin = new WC_Product_Restrictions_Admin();
            }
            add_action('woocommerce_check_cart_items', array($this, 'CheckCart'));
            $this->default_message = __('The following product(s) must be ordered in groups of %mod%. Please add another %gap% eligible product(s) to continue. %productlist%', 'woocommerce-product-restrictions');
        }
        /**
         * Load the restrictions from the database
         */
        private function LoadRestrictions()
        {
            if ($this->restrictions_loaded) {
                return;
            }
            // Attribute/Variation Restrictions
            $attribute_taxonomies = wc_get_attribute_taxonomies();
            $taxonomies = array();
            foreach ($attribute_taxonomies as $taxonomy) {
                $taxonomies[] = wc_attribute_taxonomy_name($taxonomy->attribute_name);
            }
            $restrictions = get_terms(array('taxonomy' => $taxonomies, 'meta_query' => array(array('key' => 'multiple_of', 'compare' => 'EXISTS')), 'hide_empty' => false));
            if (is_array($restrictions)) {
                foreach ($restrictions as $restriction) {
                    $this->multiple_ofs_attributes[$restriction->term_id] = $this->GetMultipleOfForAttribute($restriction->term_id);
                    if (!$this->multiple_ofs_attributes[$restriction->term_id]) {
                        unset($this->multiple_ofs_attributes[$restriction->term_id]);
                    }
                }
            }
            // Category Restrictions
            $restrictions = get_terms(array('taxonomy' => 'product_cat', 'meta_query' => array(array('key' => 'cat_multiple_of', 'compare' => 'EXISTS')), 'hide_empty' => false));
            if (is_array($restrictions)) {
                foreach ($restrictions as $restriction) {
                    $this->multiple_ofs_categories[$restriction->term_id] = $this->GetMultipleOfForCategory($restriction->term_id);
                    if (!$this->multiple_ofs_categories[$restriction->term_id]) {
                        unset($this->multiple_ofs_categories[$restriction->term_id]);
                    }
                }
            }
            $this->cart_must_be_multiple_of = intval(get_option('woocommerce_cart_multiple_of', 0));
            $this->restrictions_loaded = true;
        }
        /**
         * Get the "multiple of" setting for the specified attribute term
         *
         * @param int $term_id
         *
         * @return int|null
         */
        public function GetMultipleOfForAttribute($term_id)
        {
            $multiple = intval(get_term_meta($term_id, 'multiple_of', true));
            return $multiple > 0 ? $multiple : null;
        }
        /**
         * Get the "multiple of" setting for the specified product category
         *
         * @param int $category_id
         *
         * @return bool|int The multiple of setting, or false.
         */
        public function GetMultipleOfForCategory($category_id)
        {
            $restriction = get_term_meta($category_id, 'cat_multiple_of', true);
            if (false == $restriction) {
                return false;
            } else {
                return intval($restriction);
            }
        }
        /**
         * Get the Store-wide "multiple of" setting.
         *
         * @return int
         */
        public function GetStorewideMultipleOf()
        {
            $this->LoadRestrictions();
            return $this->cart_must_be_multiple_of;
        }
        /**
         * Set the "multiple of" setting for the specified attribute term.
         *
         * @param $term_id
         * @param int|null $multiple_of For no restriction, specify null.
         */
        public function SetMultipleOfForAttributeTerm($term_id, $multiple_of)
        {
            $term_id = absint($term_id);
            if (is_null($multiple_of)) {
                delete_term_meta($term_id, 'multiple_of');
            } else {
                $multiple_of = intval($multiple_of);
                $existing_value = get_term_meta($term_id, 'multiple_of', true);
                update_term_meta($term_id, 'multiple_of', $multiple_of, $existing_value);
            }
        }
        public function SetMultipleOfForCategory($category_id, $multiple_of)
        {
            $multiple_of = intval($multiple_of);
            if ($multiple_of) {
                update_term_meta($category_id, 'cat_multiple_of', $multiple_of);
            } else {
                delete_term_meta($category_id, 'cat_multiple_of');
            }
        }
        /**
         * Get the "checkout message" setting, which is displayed during checkout.
         *
         * @return mixed|void
         */
        public function GetMultipleOfMessage()
        {
            return get_option('woocommerce_multiple_of_message', $this->default_message);
        }
        /**
         * Check the customer's cart, and notify them if their cart contents don't match the restrictions.
         *
         * Executed during the 'woocommerce_check_cart_items' hook
         */
        public function CheckCart()
        {
            $this->LoadRestrictions();
            if ($this->GetStorewideMultipleOf() > 0) {
                // Entire cart contents must be a multiple of x. This takes precedence over all other product group rules
                $modulus = WC()->cart->cart_contents_count % $this->cart_must_be_multiple_of;
                if ($modulus != 0) {
                    $message = $this->GetMultipleOfMessage();
                    $message = str_replace('%gap%', $this->cart_must_be_multiple_of - $modulus, $message);
                    $message = str_replace('%mod%', $this->cart_must_be_multiple_of, $message);
                    $message = str_replace('%productlist%', '', $message);
                    wc_add_notice($message, 'error');
                }
            }
            foreach (WC()->cart->get_cart() as $cart_item_id => $cart_item) {
                // Product Attribute/Variation restrictions
                if (isset($cart_item['data']) && $cart_item['data'] instanceof WC_Product_Variation && is_array($cart_item['variation'])) {
                    foreach ($cart_item['variation'] as $variation_name => $variation_value) {
                        if (!$variation_value) {
                            continue;
                        }
                        if (taxonomy_exists(esc_attr(str_replace('attribute_', '', $variation_name)))) {
                            $term = get_term_by('slug', $variation_value, esc_attr(str_replace('attribute_', '', $variation_name)));
                            if (!$term) {
                                continue;
                            }
                            if (isset($this->multiple_ofs_attributes[$term->term_id])) {
                                if (isset($this->product_variation_quantities[$term->term_id]['qty'])) {
                                    $this->product_variation_quantities[$term->term_id]['qty'] += $cart_item['quantity'];
                                } else {
                                    $this->product_variation_quantities[$term->term_id]['qty'] = $cart_item['quantity'];
                                }
                                $this->product_variation_quantities[$term->term_id]['products'][$cart_item['product_id']] = true;
                            }
                        }
                    }
                }
                // Product Category restrictions
                $categories = wc_get_product_terms($cart_item['product_id'], 'product_cat');
                foreach ($categories as $category) {
                    $restriction = $this->GetMultipleOfForCategory($category->term_id);
                    if ($restriction) {
                        if (isset($this->product_category_quantities[$restriction])) {
                            // This product could be assigned to multiple categories (each of which could have matching restrictions)
                            // Only count the first one
                            if (!array_key_exists($cart_item['product_id'], $this->product_category_quantities[$restriction]['products'])) {
                                $this->product_category_quantities[$restriction]['qty'] += $cart_item['quantity'];
                            }
                        } else {
                            $this->product_category_quantities[$restriction]['qty'] = $cart_item['quantity'];
                        }
                        $this->product_category_quantities[$restriction]['products'][$cart_item['product_id']] = true;
                    }
                }
            }
            // Check the Attribute/Variation Restrictions
            foreach ($this->product_variation_quantities as $term_id => $data) {
                $quantity = $data['qty'];
                $groupof = $this->multiple_ofs_attributes[$term_id];
                $modulus = $quantity % $groupof;
                if ($modulus != 0) {
                    $message = $this->GetMultipleOfMessage();
                    $message = str_replace('%gap%', $groupof - $modulus, $message);
                    $message = str_replace('%mod%', $groupof, $message);
                    $productlist = '<ul>';
                    foreach (array_keys($data['products']) as $product_id) {
                        $product = wc_get_product($product_id);
                        $product_name = '';
                        $variation_info = '';
                        foreach (WC()->cart->get_cart() as $cart_item) {
                            if ($cart_item['product_id'] == $product_id) {
                                $product_name = $product->get_title();
                                $variation_info = WC()->cart->get_item_data($cart_item, true);
                                break;
                            }
                        }
                        $productlist .= '<li>' . sprintf(__('<a href="%1$s">%2$s</a> <small>%3$s</small>', 'woocommerce-product-restrictions'), get_permalink($product_id), $product_name, $variation_info) . '</li>';
                    }
                    $productlist .= '</ul>';
                    $message = str_replace('%productlist%', $productlist, $message);
                    wc_add_notice($message, 'error');
                }
            }
            // Check the Product Category restrictions
            foreach ($this->product_category_quantities as $groupof => $data) {
                $quantity = $data['qty'];
                $modulus = $quantity % $groupof;
                if ($modulus != 0) {
                    $message = $this->GetMultipleOfMessage();
                    $message = str_replace('%gap%', $groupof - $modulus, $message);
                    $message = str_replace('%mod%', $groupof, $message);
                    $productlist = '<ul>';
                    foreach (array_keys($data['products']) as $product_id) {
                        // get_product() exists in WooCommerce 2.0+ only
                        $product = wc_get_product($product_id);
                        $product_name = '';
                        $variation_info = '';
                        foreach (WC()->cart->get_cart() as $cart_item) {
                            if ($cart_item['product_id'] == $product_id) {
                                $product_name = $product->get_title();
                                $variation_info = WC()->cart->get_item_data($cart_item, true);
                                break;
                            }
                        }
                        $productlist .= '<li>' . sprintf(__('<a href="%1$s">%2$s</a> <small>%3$s</small>', 'woocommerce-product-restrictions'), get_permalink($product_id), $product_name, $variation_info) . '</li>';
                    }
                    $productlist .= '</ul>';
                    $message = str_replace('%productlist%', $productlist, $message);
                    wc_add_notice($message, 'error');
                }
            }
        }
    }
    function WC_Product_Restrictions()
    {
        return WC_Product_Restrictions::instance();
    }
    $GLOBALS['woocommerce_product_restrictions'] = WC_Product_Restrictions();
}
 /**
  * When an existing product category is saved/updated
  *
  * @param $term_id
  * @param $tt_id
  */
 public function SaveProductCategory($term_id)
 {
     $multiple_of = false;
     if (isset($_POST['multiple_of'])) {
         $multiple_of = intval($_POST['multiple_of']);
     }
     if ($multiple_of <= 0) {
         $multiple_of = false;
     }
     WC_Product_Restrictions()->SetMultipleOfForCategory($term_id, $multiple_of);
 }