I would like avoid a user to buy the same product twice or more, I'm not speaking about the quantity limitation during the checkout (I've already set quantity to 1 for every purchase)...for example..after a user has bought a product(He has completed the checkout process) I want that he can't buy again this product. Could you help me? Thank you in advance!
3 Answers
The best way to do this would be to use the method (How do I prevent a product from being added to the cart?) but implement custom logic to look for that product being purchased by the customer.
You can do this by using EntityFieldQuery()
and loading all of the user's orders and then you can loop through the line items and look for the product...
$orders = commerce_order_load_multiple(array(), array('uid' => $user->uid, 'status' => 'completed'));
foreach ($orders as $order) {
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
if ($line_item_wrapper->commerce_product()->sku->value() == 'SOMESKU') {
//user has bought it
}
}
}
-
i need a solution for every product, i don't want write every time the sku for each product– gioCommented Apr 27, 2016 at 22:21
This is how I have it done, it works very well!!.
Create rule, name it "avoid duplicates in cart rule; react on Event: before adding a product to the cart, save Add your conditions, in my case no conditions In Actions add Loop list: data selector: select commerce-order:commerce-line-items Variable label: List current line item Variable name: list_current_line_item save changes Components->add new component Component plugin: rule name: "avoid duplicates in cart component of rule"
Variables: Data type: Commerce line item. Label: Commerce line item label. Machine name: commerce_line_item_variable. (this is the line item from the loop in the main rule, just to explain it) Data type: Commerce product. Label: Commerce product label. Machine name: commerce_product_variable. (this is the product add to the cart, just to explain it) Save Add condition Entity has field Entity: commerce-line-item-label Field: commerce_product Save (this condition give us the actual product line item in the cart, just to explain it).
Add condition Data comparison. Data to compare: commerce-line-item-variable:commerce-product Continue Operator: equals Data value: with product-added-to-cart Save
Add action to the component: Remove an item from a list. List: commerce-line-item-variable:order:commerce-line-items Item to remove: commerce-line-item-variable (This action remove the duplicated product (the old) from the cart, just to explain it).
Add another action to the component: System: Show a message on the site Message: Duplicated item has been deleted from the cart. Save
Now come on, exit from the component rule and return again to main rule, edit it again.
Add action to the loop Select action to add: Component: "avoid duplicates in cart component of rule". and set the variables. Commerce line item label: lista-current-line-item Commerce product label: commerce-product Save
And you are very happy, no more repeated products in cart. :) Enjoy
This is what I figured out based on some other answers. Modify the part around 'form_set_error' to customize what happens. If you need multilingual support you'll need to modify this further because I did a lousy job of that part. I might have gone overboard assuming a cart line item can contain multiple products, so I've added comments to point that out - it could probably be simplified in most cases.
This would go in a custom module (good idea to have one for each site you make), and change YOURMODULE accordingly. Don't forget to clear your cache so the hook function is found by Drupal.
/**
* Implements hook_form_FORM_ID_alter().
*/
function YOURMODULE_form_commerce_cart_add_to_cart_form_alter(&$form, &$form_state) {
array_unshift($form['#validate'], 'YOURMODULE_cart_validate');
}
/*
* Callback used with YOURMODULE_form_commerce_cart_add_to_cart_form_alter().
*/
function YOURMODULE_cart_validate(&$form, &$form_state) {
// Pull up the current cart data.
$order = commerce_cart_order_load($form_state['values']['uid']);
// This is not a multi-lingual site so I just use 'und' as the language.
// It seems $order contains some language info, but mine is blank so I
// don't know how to use it properly.
$lang = LANGUAGE_NONE;
// This is the id of the product the user is attempting to add to cart.
// This handles multiple products being added at once, which I've never seen
// before but it's set up like an array so who knows...
$attempted_product_ids = array();
foreach ($form_state['line_item']->commerce_product[$lang] as $product) {
$attempted_product_ids[] = $product['product_id'];
}
// Prepare to collect cart line items.
$ids = array();
// Check if cart has items.
if (!empty($order->commerce_line_items[$lang])) {
// Collect cart line item ids.
foreach ($order->commerce_line_items[$lang] as $line_item) {
$ids[] = $line_item['line_item_id'];
}
// Get the line items based on the line item ids.
$line_items = commerce_line_item_load_multiple($ids);
// Check each line item.
foreach ($line_items as $line_item) {
// Check the line item has products. It should though, right?
if (!empty($line_item->commerce_product[$lang])) {
// Check each product. I don't know if a line item can have multiple
// products, but it is an array so I guess it's possible?
foreach ($line_item->commerce_product[$lang] as $existing_product) {
// Compare the existing product id and the attempted add to cart product.
if (in_array($existing_product['product_id'], $attempted_product_ids)) {
// Load the product so we can get its title and sku for the error message.
$product = commerce_product_load($existing_product['product_id']);
// Produce an error message and prevent submission of the 'add to cart' form.
form_set_error('product_id', t("You've already added %product (%sku) to !cart.", array(
'%product' => $product->title,
'%sku' => $product->sku,
'!cart' => l(t('your cart'), 'cart/my'),
)));
}
}
}
}
}
}