I'm currently in a problem where I have to add option in Cart with checkbox (for each item in Cart) which will change item's price with one from custom attribute.
This is an illustration of it (I have already created custom field, just need price updating function when button "Update cart" is clicked)
Code for displaying checkbox for each item (/woocommerce/templates/cart/cart.php):
<td class="product-url">
<?php
$html = sprintf( '<div class="lorem"><input type="checkbox" name="cart[%s][lorem]" value="%s" size="4" class="input-text url text" /> Lorem price</div>', $cart_item_key, esc_attr( $values['url'] ) );
echo $html;
?>
</td>
Here I'm assuming that lorem price is stored in custom meta field associated with meta_key your_custom_meta_field
Use following code in your theme's function.php file
add_action( 'woocommerce_before_calculate_totals', 'my_custom_calculate_totals' );
function my_custom_calculate_totals( $cart ) {
if ( ! empty( $cart->cart_contents ) ) {
$lorem_price = array();
if ( ! empty( $_REQUEST['cart'] ) ) { // check if any of the checkboxes is checked
WC()->session->set( 'my_lorem_price', $_REQUEST['cart'] ); // set all checkboxes information in session
$lorem_price = $_REQUEST['cart'];
}
if ( empty( $lorem_price ) ) {
$lorem_price = WC()->session->get( 'my_lorem_price' ); // fetch all checkboxes information from session
}
if ( empty( $lorem_price ) ) {
return; // don't do anything if any of the checkboxes is not checked
}
foreach ( $cart->cart_contents as $cart_item_key => $cart_item ) {
if ( isset( $lorem_price[ $cart_item_key ]['lorem'] ) ) {
// Use following line if lorem price is set at variation level
$id = ( ! empty( $cart_item['variation_id'] ) && $cart_item['variation_id'] > 0 ) ? $cart_item['variation_id'] : $cart_item['product_id'];
// Use following line if lorem price is set at product level
// $id = $cart_item['product_id'];
$new_price = get_post_meta( $id, 'your_custom_meta_field', true ); // fetch price from custom field
$cart_item['data']->price = $new_price;
}
}
}
}
Related
In my functions.php, I am trying to add a sold out message in the drop down menu for my product variants. So for example if I have shirt that has variants of small, medium and large and the large is out of stock, in the drop down menu the user should see the large option is disabled and contains a sold out message next to 'Large'. The other variants should remain active.
The issue I have with my code below is the following:
The code that is commented, this disables the correct product variant that is out of stock, but doesn't add the sold out message.
The active code does add the sold out message but it disables all product variants even though it's only one variant that is out of stock.
How can I fix the code to do what I need it to do?
/**
* Disable out of stock variations
* https://github.com/woocommerce/woocommerce/blob/826af31e1e3b6e8e5fc3c1004cc517c5c5ec25b1/includes/class-wc-product-variation.php
* #return Boolean
*/
// function wcbv_variation_is_active( $active, $variation ) {
// if( ! $variation->is_in_stock() ) {
// return false;
// }
// return $active;
// }
// add_filter( 'woocommerce_variation_is_active', 'wcbv_variation_is_active', 10, 2 );
add_action( 'woocommerce_variation_is_active', 'woocommerce_sold_out_dropdown' );
function woocommerce_sold_out_dropdown() {
?>
<script type="text/javascript">
jQuery( document ).bind( 'woocommerce_update_variation_values', function() {
jQuery( '.variations select option' ).each( function( index, el ) {
var sold_out = '<?php _e( 'sold out', 'woocommerce' ); ?>';
var re = new RegExp( ' - ' + sold_out + '$' );
el = jQuery( el );
if ( el.is( ':disabled' ) ) {
if ( ! el.html().match( re ) ) el.html( el.html() + ' - ' + sold_out );
} else {
if ( el.html().match( re ) ) el.html( el.html().replace( re,'' ) );
}
} );
} );
</script>
<?php
}
You can get what you need with this code (your code):
// disable options for unavailable variants
add_filter( 'woocommerce_variation_is_active', 'wcbv_variation_is_active', 10, 2 );
function wcbv_variation_is_active( $active, $variation ) {
if ( ! $variation->is_in_stock() ) {
return false;
}
return $active;
}
And now, to change the name of each individual option based on stock status you can use the woocommerce_variation_option_name hook like so:
add_filter( 'woocommerce_variation_option_name','add_stock_status_after_option_name', 10, 1 );
function add_stock_status_after_option_name( $option ) {
// only in frontend
if ( is_admin() ) {
return $option;
}
// get variable product object
global $product;
$variation_ids = $product->get_children();
foreach ( $variation_ids as $variation_id ) {
$variation = wc_get_product( $variation_id );
$variation_attributes = $variation->get_variation_attributes();
foreach ( $variation_attributes as $key => $value ) {
// slugify option name
$option_slug = sanitize_title( $option );
// check if the current option is equal to the variation slug
if ( $value == $option_slug ) {
// check if it is out of stock
if ( ! $variation->is_in_stock() ) {
return $option . ' (Out of stock)';
}
}
}
}
return $option;
}
The code has been tested and works correctly. It needs to be added to your theme's functions.php.
I'm using the code below to add three buttons to the WooCommerce checkout page so customers can leave a donation. It's working as it should be, but it would be great if I can highlight one of the buttons when a customer adds a donation to their cart and disable the other two.
Any thoughts on how I can get this to work?
add_action( 'woocommerce_review_order_before_submit', 'charity_checkout_add_on', 9999 );
function charity_checkout_add_on() {
$product_ids = array( 59355 );
$in_cart = false;
foreach( WC()->cart->get_cart() as $cart_item ) {
$product_in_cart = $cart_item['variation_id'];
if ( in_array( $product_in_cart, $product_ids ) ) {
$in_cart = true;
break;
}
}
if ( ! $in_cart ) {
echo '<div id="charity-donation">';
echo '<h6>Heading text</h6>';
echo '<p>Explanation</p>';
echo '<a class="charity-button" href="?add-to-cart=59356">€0,50</a><a class="charity-button" href="?add-to-cart=59357">€1,00</a><a class="charity-button last" href="?add-to-cart=59358">€2,50</a>';
echo '</div>';
}
}
Instead of hard-coding the HTML for the buttons you can loop over the $product_ids array to fetch and build up the HTML dynamically. This will work for both simple and variable products. You should always escape these values where possible: see https://developer.wordpress.org/themes/theme-security/data-sanitization-escaping/
You could then simply add a css class to the button if the product is in the cart by comparing its ID against any of the IDs in the cart.
Issues with add to cart in URL
If you don't redirect to the cart page after the product has been added, when the checkout refreshes the ?add-to-cart=ID query parameter will remain in the URL. This means that if you refresh the checkout page, the product will be added again. To solve this, either tick "Redirect to the basket page after successful addition" in Woocommerce Settings > Products > General or change the add-t-cart url to something like yourdomain.com/cart/?add-to-cart=ID
Try:
add_action( 'woocommerce_review_order_before_submit', 'charity_checkout_add_on', 20 );
function charity_checkout_add_on() {
$product_ids = array( 59356, 59357, 59358 );
$products_in_cart = array();
$button_data = array();
// Gather product cart items in array
foreach( WC()->cart->get_cart() as $cart_item ) {
$cart_product = $cart_item['data'];
$products_in_cart[] = $cart_product->get_id();
}
// Dynamically generate buttons
$button_html = '';
$last_button = end( $product_ids );
foreach( $product_ids as $product_id ) {
if( $product = wc_get_product( $product_id ) ) {
$in_cart = ( in_array( $product->get_id(), $products_in_cart ) ) ? true : false;
$button_classes = 'charity-button';
if( $in_cart ) {
$button_classes.= ' disabled';
}
if( $product_id === $last_button ) {
$button_classes.= ' last';
}
$button_html .= sprintf(
'<a class="%s"%s>%s</a>',
esc_attr( $button_classes ),
( $in_cart ) ? '' : esc_url( 'href="?add-to-cart=' . $product->get_id() . '"' ),
esc_html( get_woocommerce_currency_symbol() . $product->get_price() )
);
};
}
?>
<h4>Make a Donation?</h4>
<div id="charity-donation">
<h6>Heading text</h6>
<p>Explanation</p>
<?php echo $button_html; ?>
</div>
<?php
}
Your css class then could be something like:
.charity-button.disabled {
cursor: not-allowed;
opacity: 0.5;
}
So i first created 2 fields, the custom field to display the calculated price and a hidden field that has the percentage value
/*Custom price field - to create custom field for simple product*/
add_action( 'woocommerce_product_options_pricing', 'wc_cost_product_field' );
function wc_cost_product_field() {
woocommerce_wp_text_input(
array(
'id' => 'cost_price',
'name' =>'cost_price',
'class' => 'wc_input_price short',
'desc_tip' => 'true',
'description' => __( 'Your Profit price is calculated as ((Vendor price x 15%) + 500 + vendor price )', 'woocommerce' ),
'label' => __( 'Total Price with Profit', 'woocommerce' ) . ' (' . get_woocommerce_currency_symbol() . ')'
)
);
woocommerce_wp_hidden_input(
array(
'id' => '_hidden_field[' . $post->ID . ']',
'name' => 'per',
'value' => 0.15
)
);
}
So i saved the custom field to always display
/*Custom price field - to save custom field for simple product*/
add_action( 'save_post', 'wc_cost_save_product' );
function wc_cost_save_product( $product_id ) {
// stop the quick edit interferring as this will stop it saving properly, when a user uses quick edit feature
if (wp_verify_nonce($_POST['_inline_edit'], 'inlineeditnonce'))
return;
// If this is a auto save do nothing, we only save when update button is clicked
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;
if ( isset( $_POST['cost_price'] ) ) {
if ( is_numeric( $_POST['cost_price'] ) )
update_post_meta( $product_id, 'cost_price', $_POST['cost_price'] );
} else delete_post_meta( $product_id, 'cost_price' );
$hidden = $_POST['_hidden_field'][ $post_id ];
if( ! empty( $hidden ) ) {
update_post_meta( $post_id, '_hidden_field', esc_attr( $hidden ) );
}
}
?>
Then i added the script that will call and trigger the value then render it to the custom field
<script>
const calculateprofit_price = (profitPrice, percent) => {
profitPrice = parseFloat(profitPrice);
percent = parseFloat(pecent);
return ((profitPrice * percent) + 500 + profitPrice).toFixed(2); // profit price
}
const $price = $('input[name="_regular_price"]'),
$per = $('input[name="per"]'),
$profit_price = $('input[name="cost_price"]');
$price.add( $per ).on('input', () => { // price and percent inputs events
let profit_price = $price.val(); // Default to profit price
if ( $per.val().length ) { // if value is entered- calculate profit_price
profit_price = calculateprofit_price($price.val(), $per.val());
}
$profit_price.val( profit_price );
});
$price.trigger('input');
</script>
I have checked online to see available codes to help but to no avail, pls help.
This is an image of what I'm trying to achieve
displaying calculated regular price in custom field
The context:
On my website users can customize clothing products. Along the customer funnel, it is asked to the user to input a quantity. This quantity is then used when our team receives the order to generate a quote. Nevertheless, it appears that the quantity is not stored as it always returns 1, even when the user inputs another number e.g. 100. I cannot find a way to solve this.
Below you will find code parts that include the quantity dimension. If you see any corrections to be made feel free to mention them.
add_filter( 'wc_add_to_cart_message_html', 'epopey_wc_add_to_cart_message_html', 10, 3);
function epopey_wc_add_to_cart_message_html ( $message, $products, $show_qty ){
$titles = array();
if ( ! is_array( $products ) ) {
$products = array( $products => 1 );
}
if ( ! $show_qty ) {
$products = array_fill_keys( array_keys( $products ), 1 );
}
foreach ( $products as $product_id => $qty ) {
/* translators: %s: product name */
$titles[] = apply_filters( 'woocommerce_add_to_cart_qty_html', ( $qty > 1 ? absint( $qty ) . ' × ' : '' ), $product_id ) . apply_filters( 'woocommerce_add_to_cart_item_name_in_quotes', sprintf( _x( '“%s”', 'Item name in quotes', 'woocommerce' ), strip_tags( get_the_title( $product_id ) ) ), $product_id );
$count += $qty;
}
$titles = array_filter( $titles );
Backorder notification
if ( $_product->backorders_require_notification() && $_product->is_on_backorder( $cart_item['quantity'] ) ) {
echo '<p class="backorder_notification">' . esc_html__( 'Available on backorder', 'norebro' ) . '</p>';
}
?>
</h4>
</td>
<td class="product-quantity" data-title="<?php esc_html_e( 'Quantity', 'norebro' ); ?>">
<?php
if ( $_product->is_sold_individually() ) {
$product_quantity = sprintf( '1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key );
} else {
$product_quantity = woocommerce_quantity_input( array(
'input_name' => "cart[{$cart_item_key}][qty]",
'input_value' => $cart_item['quantity'],
'max_value' => $_product->backorders_allowed() ? '' : $_product->get_stock_quantity(),
'min_value' => '0'
), $_product, false );
}
echo apply_filters( 'woocommerce_cart_item_quantity', $product_quantity, $cart_item_key, $cart_item );
?>
</td>
email details to customer
<td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
<?php
$qty = $item->get_quantity();
$refunded_qty = $order->get_qty_refunded_for_item( $item_id );
if ( $refunded_qty ) {
$qty_display = '<del>' . esc_html( $qty ) . '</del> <ins>' . esc_html( $qty - ( $refunded_qty * -1 ) ) . '</ins>';
} else {
$qty_display = esc_html( $qty );
}
echo wp_kses_post( apply_filters( 'woocommerce_email_order_item_quantity', $qty_display, $item ) );
?>
</td>
order details
<td class="product-quantity">
<?php echo apply_filters( 'woocommerce_order_item_quantity_html', ' <strong class="product-quantity">' . sprintf( '%s', $item->get_quantity() ) . '</strong>', $item ); ?>
</td>
</tr>
<?php if ( $show_purchase_note && $purchase_note ) : ?>
<tr class="product-purchase-note">
<td colspan="2"><?php echo wpautop( do_shortcode( wp_kses_post( $purchase_note ) ) ); ?></td>
Table with order details
<table class="woo-my-orders-table woo-details woo-my-content">
<thead>
<tr>
<th class="product-name"><?php esc_html_e( 'Product', 'norebro' ); ?></th>
<th class="product-quantity"><?php esc_html_e( 'Quantity', 'norebro' ); ?></th>
</tr>
</thead>
<tbody>
<?php
foreach( $order->get_items() as $item_id => $item ) {
$product = apply_filters( 'woocommerce_order_item_product', $order->get_product_from_item( $item ), $item );
wc_get_template( 'order/order-details-item.php', array(
'order' => $order,
'item_id' => $item_id,
'item' => $item,
'show_purchase_note' => $show_purchase_note,
'purchase_note' => $product ? get_post_meta( $product->get_id(), '_purchase_note', true ) : '',
'product' => $product,
) );
}
?>
<?php do_action( 'woocommerce_order_items_table', $order ); ?>
</tbody>
</table>
Congrats if you read until here, hope there is enough details. Thanks for your time and help.
Edit #1
Could the below code lines be a starting point to solve the problem ?
HTML
<div type="submit" class="wc-proceed-to-checkout" onClick="quantitySubmit(this.form)">
Valider le devis
JS
function quantitySubmit(form){
return (form.submit()){
window.location.href = "https://epopey.co/validation-du-devis/"
}
else {
text = "Please input a number"
}
}
The page that allows a change in quantity isn't updating the cart when it moves to the validate quote page. The button that was added to link to that page doesn't first perform the woo commerce cart update. See below:
The input of type submit must be "clicked" so the page can post back to itself and update the cart before the browser is redirected to the new page.
I'd recommend a simple javascript function in the onclick attribute of the div (class = wc-proceed-to-checkout) that submits the form and returns true/false on success/failure and allows redirection to "https://epopey.co/validation-du-devis/" for success and gives whatever error message you decide on failure.
To redirect in JS: https://www.w3schools.com/howto/howto_js_redirect_webpage.asp
If you need help writing the javascript to submit your form and handle the return value, search for answers on this site, I know they exist/also on Google.
There are numerous other ways to solve this as well - hard to give a best practice answer without knowing your code base/the structure of the theme. Hopefully identifying the problem is enough for you to solve on your own.
TLDR; your validate quote button overrides a default submission button to update the cart with increased/decreased quantities. Make sure the form on this page posts before the page is redirected to the next, or you'll lose the changed quantities.
I am working on gravity forms add-on. I am saving all the values of gform fields in my database successfully after form submission using this hook gform_after_submission. Now I want to save the total pricing value but I don't get it I also see that the total pricing value coming from span.
So i search a lot but nothing found except some java script code:
<script type="text/javascript">
gform.addFilter( 'gform_product_total', function(total, formId) {
return total;
} );
</script>
But i don't get that how can i get this value in gform_after_submission hook
So please give me some suggestions
check bellow code . it's meet your requirement .
function saiful_gf_remove_money_symbol( $s ) {
return GFCommon::to_number( $s );
}
add_filter( 'saiful_gf_remove_money_symbol', 'saiful_gf_remove_money_symbol' );
add_filter( 'gform_product_info', 'add_fee', 10, 3 );
function add_fee( $product_info, $form, $lead ) {
$loop_price = 0;
if( isset( $product_info['products'] ) && count( $product_info['products'] ) > 0 ){
foreach ( $product_info['products'] as $data ){
if( isset( $data['options'] ) && count( $data['options'] ) > 0 ){
foreach ( $data['options'] as $key => $option ) {
$loop_price += floatval( $option['price'] );
}
}
// Save From Here total amount
$new_price = apply_filters( 'saiful_gf_remove_money_symbol',$data['price']) + $loop_price ;
}
}
return $product_info;
}