Custom Woocommerce cart add has wrong item count - php

I'm trying to add a product to a woocommerce cart with a wp_loaded action after I submit a custom add to cart form.
add_action( 'wp_loaded', 'custom_process_form' );
function custom_process_form(){
global $woocommerce;
if(isset($_POST["addcoupon"])){
foreach($_POST as $key=>$value){
if($key=="addcoupon"){
continue;
}
$valarr=explode("_",$key);
if ($valarr[0]=="couponid"){
$woocommerce->cart->add_to_cart($valarr[1],1);
$count++;
}
}
}
}
It works fine except when it first loads, the cart thinks that I added 2 products instead of 1. For some reason, its running WC_Cart->calculate_totals() twice and $this->cart_contents_count is not reset to zero, so the quantity gets twice. (When I reload the page, the cart shows the correct number of items)
What is the correct way to add a product with a custom form? I can't find any examples of this. Am I using the wrong action?

I experimented on my own install and can confirm the same is happening to me. For me calculate_totals() is getting called once by the add to cart process and then again by the WooCommerce Subscriptions plugin getting the cart from the session. Do you have this plugin installed?
That said I don't think calling calculate_totals() is really a problem. The real problem is that $this->cart_contents_count should reset. I got around this by adding this code to functions.php.
function reset_quantities( $cart )
{
$cart->cart_contents_count = 0;
}
add_action( 'woocommerce_before_calculate_totals', 'reset_quantities' );
EDIT: Actually that doesn't work for me. The reason is that the WooCommerce Subscriptions plugin is calling calculate_totals() through the 'woocommerce_before_calculate_totals' action. This prevents other functions on the same action from firing. This is mentioned at this ticket. https://core.trac.wordpress.org/ticket/17817
Try this instead
function reset_quantities( $cart_object )
{
$cart = $cart_object->get_cart();
$cart_object->cart_contents_count = 0;
foreach ( $cart as $cart_item_key => $values ) {
$cart_object->cart_contents_count += $values['quantity'];
}
}
add_action( 'woocommerce_after_calculate_totals', 'reset_quantities', 10, 1 );
EDIT2 I just looked at the latest commit of WooCommerce on Github and calculate_totals() has changed a lot. You may want to try downloading that to see if it solves your problem.

Related

Disable products in same category if an item is added to the cart - WooCommerce

I've been looking for a solution to disable certain products from the same category if one of the items is added to the cart.
For example: I have 1 category with products from A to E.
If I add product A to the cart I want to disable products B and E, so they can't be added to the cart during the same shopping session.
Is there a way to achieve this?
Many thanks.
Yes , you actually have two options ,one is to customize your own plugin and insert it to your website but this actually takes time .The other way is to download a wholesale and b2b plugin , these plugins usually contain features as the one you are looking for , so you will be able to activate the feature that you want .
You can do that by validating the cart whenever an item is added to it.
You can do that by editing your theme (or child theme) functions.php file or by using your custom plugin.
You can use the following hook 'woocommerce_add_to_cart_validation'
add_filter( 'woocommerce_add_to_cart_validation', 'allowed_item_category_in_the_cart', 10, 2 );
function allowed_item_category_in_the_cart( $passed, $product_id) {
$product = wc_get_product($product_id);
// Get product categories
$product_category_ids = $product->get_category_ids();
// Then you iterate through each cart item
foreach (WC()->cart->get_cart() as $cart_item_key=>$cart_item ){
$cart_product_id = $cart_item['product_id'];
$cart_product = wc_get_product($cart_product_id);
$cart_product_category_ids = $cart_product->get_category_ids();
if (array_intersect($cart_product_category_ids, $product_category_ids))
return false;
}
return true;
}
PS: I just wrote this code. I didn't have time to test it yet.
Let me know your feedback.

Move cart.php and cart-totals.php to checkout page

To reduce the number of redirects on my site, i added the cart page on the same page as the checkout. and used this code to do it:
function cart_on_checkout_page_only() {
if ( is_wc_endpoint_url( 'order-received' ) ) return;
// add woocomerce cart shortcode on checkout page
echo do_shortcode('[woocommerce_cart]');
}
// Cart and Checkout same page
add_action( 'woocommerce_before_checkout_form', 'cart_on_checkout_page_only', 5 );
The problem is in the layout specifically, on devices it is too far from the order items as the order total is at the bottom of the page near the checkout button. I got a lot of complaints because people are too lazy to scroll down to see the coupon application in the cart at the bottom of the page
so I made the following modification:
I copied the woocommerce plugin files woocommerce/template/cart/cat.php and cart-totals.php
and pasted it into my theme in themes/mytheme/template/cart/custom-cart.php
and adapted it in the code above:
function cart_on_checkout_page_only() {
if ( is_wc_endpoint_url( 'order-received' ) ) return;
// add custom cart template on checkout page
get_template_part('template/cart/custom-cart');
}
// Cart and Checkout same page
add_action( 'woocommerce_before_checkout_form', 'cart_on_checkout_page_only', 5 );
my code did the job, but I believe there should be a simpler and more elegant way to make this kind of change. it's possible to make more simpler?
by the way I'm using a child storefront theme.

Hide Button only on External/Affiliate Product Page / NOT in shop

I would like to hide all buttons only for Eternal/Affiliate and only on the produce page
TLDR
On my site I have a few Pricing tables where the customer can select the appropriate one and then add that item to the cart.
The issue is trying to make an empty product where you cannot purchase it but the pricing table is in the description. So I had to user Eternal/Affiliate product. Which works perfectly as the URL is the product page itself which is great for the shop.
The issue is on the product page you can see a button which says add to cart.
I would like to hide this button only for Eternal/Affiliate and only on the produce page.
You see I already have this code below but obviously I do not want to restrict it to ID numbers and variable products.
Update
so the code below works but if I put a URL link in on the product page then it does not work. Unfortunately now a little bit stumped.
What would be great is if I could remove the button and replace it with some text for example please read below.
function woocommerce_add_aff_link(){
$product = wc_get_product(get_the_ID());
if ($product->is_type('external'))
echo '<a href="' .
$product->get_product_url() .
'" class="woocommerce-LoopProductImage-link">';
}
Update +
After some digging around I found that it is actually more logical to create a new product however the solution below works perfectly:
Reference : https://www.businessbloomer.com/woocommerce-how-to-create-a-new-product-type/
To remove add to cart button on single external product pages use the following:
add_action( 'woocommerce_single_product_summary', 'remove_external_product_add_to_cart_button', 4 );
function remove_external_product_add_to_cart_button(){
global $product;
if ( ! is_a($product, 'WC_Product') ) {
$product = wc_get_product(get_the_ID());
}
if ( $product->is_type('external') ) {
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );
}
}
Code goes in functions.php file of the active child theme (or active theme). TIt should work.

How to make a redirection to checkout on a custom add to cart button in WooCommerce

I'm using WooCommerce and added the following code to functions.php:
add_filter( 'woocommerce_product_add_to_cart_url', 'custom_fix_for_individual_products', 10, 2 );
function custom_fix_for_individual_products( $add_to_cart_url, $product ){
$add_to_cart_url = wc_get_checkout_url();
return $add_to_cart_url;
}
What I would expect is that when a user adds a product to their basket: this code will redirect the user to the checkout page without actually adding the product to the basket. However, it still adds the product to the basket. What is wrong with this code?
Background: I'm using Uncaught Error: Call to a member function generate_cart_id() on null answer code but it doesn't work correctly for the above line, which is why I'm now working with only that line. There's no error in the server logs.
Based on your comment, to get a redirection to checkout page for a custom add to cart button, you could change the link to something like https://example.com/checkout/?add-to-cart=99 where 99 is the product Id that you want to add to cart.
So the code for your custom add to cart button link will be like (where 99 is the product Id):
$product_id = 99;
$url = wc_get_checkout_url() . '?add-to-cart=' . $product_id;

Disabling cart page

I would like to disable the WooCommerce cart functionality by all means.
My shop has only a single product, so cart is doesn't really needed.
My desired flow is Click on buy button -> go to checkout page.
Incase user goes back and redo the same process, checkout page will not show 2 products in summary buy just 1.
Any tips on how to achieve this smoothly ?
Thanks,
You will Need 4 code snippets:
1) Disabling quantities buttons (on product page):
add_filter( 'woocommerce_is_sold_individually', '__return_true' );
2) Add-to-cart validation, allowing just one product in cart:
add_action( 'woocommerce_add_to_cart_validation', 'check_product_is_in_cart' );
function check_product_is_in_cart() {
WC()->cart->empty_cart();
return true;
}
3) Checkout redirect customer when your product is added to cart (with modern syntax):
add_filter ('add_to_cart_redirect', 'redirect_to_checkout');
function redirect_to_checkout() {
return WC()->cart->get_checkout_url();
// OR ALSO:
// return get_permalink(get_option('woocommerce_checkout_page_id'));
}
The code comme from this answer (with the correct new syntax): Woocommerce add to cart button redirect to checkout
4) Redirect Cart page to Checkout page (in case of):
add_action('template_redirect', 'skip_cart_page_redirection_to_checkout');
function skip_cart_page_redirection_to_checkout() {
if(is_cart()){
wp_redirect(WC()->cart->get_checkout_url());
// OR ALSO:
// wp_redirect( get_permalink( get_option( 'woocommerce_checkout_page_id' ) ) );
exit; // This is mandatory with wp_redirect()
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Code is tested and works.
Disabling redirect to Cart on add-to-cart action and Ajax add-to-cart on Shop page and archives pages (optional)
You can also disable some settings in WooCommerce > Settings > Products > Display (tab).
Optionally keep that 2 options disabled (and save settings):
If you need to skip the cart page the easiest way it is to go to the Woocommerce->Settings->Checkout and set Cart Page to "Checkout" page
Or use this snippet
add_filter('add_to_cart_redirect', 'themeprefix_add_to_cart_redirect');
function themeprefix_add_to_cart_redirect() {
global $woocommerce;
$checkout_url = $woocommerce->cart->get_checkout_url();
return $checkout_url;
}
Don't forget to disable Ajax Add to cart and Redirect to the cart page after successful addition in Woocommerce->Settings->Products->Display
If you use the first method you can check the Redirect to the cart page after successful addition.
For the limit one item in checkout use this snippet.
In this way, the customer only can buy one item, if the customer goes to another product and tries to buy it, the cart will be cleaned and the last item added.
add_filter( 'woocommerce_add_cart_item_data', 'woo_custom_add_to_cart' );
function woo_custom_add_to_cart( $cart_item_data ) {
global $woocommerce;
$woocommerce->cart->empty_cart();
return $cart_item_data;
}

Categories