In woocommerce, I am just using simple products and I have a special product "free trial package" from a product category "A", which I'm willing to:
This means the customer only be able to get the "free trial package" only if it's the first purchase of that product category "A".
My code doesn't seem to work properly and it only do the 1st point.
The other problem is that if the user order finishes, next time user open cart it shows a weird message: "you cant select this product anymore, you already bought this package".It's like Woocommerce cache problem or something…
Here is my code so far:
function sv_disable_repeat_purchase( $purchasable, $product ) {
$non_purchasable = 190;
$product_id = $product->is_type( 'variation' ) ? $product->variation_id : $product->id;
if ( $product_id != $non_purchasable ) {
$purchasable = true;
}
if ( wc_customer_bought_product( wp_get_current_user()->user_email, get_current_user_id(), $product_id ) ) {
$purchasable = false;
}
if ( $purchasable && $product->is_type( 'variation' ) ) {
$purchasable = $product->parent->is_purchasable();
}
return $purchasable;
}
add_filter( 'woocommerce_variation_is_purchasable', 'sv_disable_repeat_purchase', 10, 2 );
add_filter( 'woocommerce_is_purchasable', 'sv_disable_repeat_purchase', 10, 2 );
Any help is appreciated.
Update 2
Based on "Check if a customer has purchased a specific products in WooCommerce" answer code, the following will prevent purchases from a "trial package" product, if it has already been purchased once, or if any product for a defined product category "A" has been already purchased.
Your actual code is outdated since Woocommerce 3 and there some mistakes…
I use a custom conditional function inspired from wc_customer_bought_product()
source code, to check if customer can buy the "free trial" product. This function will check as well for a product ID or for a product category:
function has_bought_items( $user_id = 0, $product_ids = 0, $categories_slugs = '' ) {
global $wpdb;
$customer_id = $user_id == 0 || $user_id == '' ? get_current_user_id() : $user_id;
$statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
if ( is_array( $product_ids ) ) {
$product_ids = implode(',', $product_ids);
}
if ( $product_ids != ( 0 || '' ) ) {
$query_in = "IN ($product_ids)";
}
else {
$categories = is_array( $categories_slugs ) ? implode("','", $categories_slugs) : $categories_slugs;
$query_in = "IN (
SELECT DISTINCT products.ID FROM {$wpdb->prefix}posts AS products
INNER JOIN {$wpdb->prefix}term_relationships as term_rel
ON products.ID = term_rel.object_id
INNER JOIN {$wpdb->prefix}term_taxonomy as term_tax
ON term_rel.term_taxonomy_id = term_tax.term_taxonomy_id
INNER JOIN {$wpdb->prefix}terms as terms
ON term_tax.term_taxonomy_id = terms.term_id
WHERE products.post_status = 'publish'
AND term_tax.taxonomy = 'product_cat'
AND terms.slug IN ('$categories')
)";
}
// Count the number of products
$product_count_query = $wpdb->get_var( "
SELECT DISTINCT COUNT(orders.ID)
FROM {$wpdb->prefix}posts AS orders
INNER JOIN {$wpdb->prefix}postmeta AS order_meta
ON orders.ID = order_meta.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items
ON orders.ID = order_items.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_itemmeta
ON order_items.order_item_id = order_itemmeta.order_item_id
WHERE orders.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
AND order_meta.meta_key = '_customer_user'
AND order_meta.meta_value = $customer_id
AND order_itemmeta.meta_key IN ( '_product_id', '_variation_id' )
AND order_itemmeta.meta_value $query_in
" );
// Return a boolean value if count is higher than 0
return $product_count_query > 0 ? true : false;
}
Your revisited code using the custom conditional function above:
// add_filter( 'woocommerce_variation_is_purchasable', 'trial_package_purchasable_once', 10, 2 );
add_filter( 'woocommerce_is_purchasable', 'trial_package_purchasable_once', 10, 2 );
function trial_package_purchasable_once( $purchasable, $product ) {
// HERE set the free trial product Id to be purchased only once (can be an array of Ids too)
$free_trial_id = 50;
// HERE set the specific product category SLUG (could be an array of slugs too)
$categories = array('lettering'); // Only slug terms
if ( $product->get_id() == $free_trial_id ) {
// Check if any item of the specific product category has already been purchased once
if ( has_bought_items( get_current_user_id(), '', $categories ) ) {
return false;
}
// Check if the free trial product has already been purchased once
elseif ( has_bought_items( get_current_user_id(), $free_trial_id ) ) {
return false;
}
}
}
return $purchasable;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
To handle product categories Term IDs instead of Term Slugs use the following conditional function instead:
// For Product category(ies) Id(s)
function has_bought_items( $user_id = 0, $product_ids = 0, $categories_ids = '' ) {
global $wpdb;
$customer_id = $user_id == 0 || $user_id == '' ? get_current_user_id() : $user_id;
$statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
if ( is_array( $product_ids ) ) {
$product_ids = implode(',', $product_ids);
}
if ( $product_ids != ( 0 || '' ) ) {
$query_in = "IN ($product_ids)";
}
else {
$categories = is_array( $categories_ids ) ? implode(',', $categories_ids) : $categories_ids;
$query_in = "IN (
SELECT DISTINCT products.ID FROM {$wpdb->prefix}posts AS products
INNER JOIN {$wpdb->prefix}term_relationships as term_rel
ON products.ID = term_rel.object_id
INNER JOIN {$wpdb->prefix}term_taxonomy as term_tax
ON term_rel.term_taxonomy_id = term_tax.term_taxonomy_id
INNER JOIN {$wpdb->prefix}terms as terms
ON term_tax.term_taxonomy_id = terms.term_id
WHERE products.post_status = 'publish'
AND term_tax.taxonomy = 'product_cat'
AND terms.term_id IN ($categories)
)";
}
// Count the number of products
$product_count_query = $wpdb->get_var( "
SELECT DISTINCT COUNT(orders.ID)
FROM {$wpdb->prefix}posts AS orders
INNER JOIN {$wpdb->prefix}postmeta AS order_meta
ON orders.ID = order_meta.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items
ON orders.ID = order_items.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_itemmeta
ON order_items.order_item_id = order_itemmeta.order_item_id
WHERE orders.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
AND order_meta.meta_key = '_customer_user'
AND order_meta.meta_value = $customer_id
AND order_itemmeta.meta_key IN ( '_product_id', '_variation_id' )
AND order_itemmeta.meta_value $query_in
" );
// Return a boolean value if count is higher than 0
return $product_count_query > 0 ? true : false;
}
It's better alternative for foreign languages with special characters.