Copy WooCommerce sale price meta to a custom field - woocommerce

I am trying to get woocommerce sale price meta field from the plugin woocommerce price based on country and then copy it to another custom field. The code seems to create an error? Am I getting the price in the wrong way?
if( ( $value = $product->get_meta( 'canada_sale_price' ) && ! $product->get_meta( 'customfieldprice' ) ){
$product->update_meta_data( 'customfieldprice', $value );
}
I tried also using the native regular and sale price from WooCommerce but no success...
if( ( $value = $product->get_price() || $value = $product->get_regular_price() ) ) { update_post_meta( $product_id, 'customfieldprice', $value ); }

You can use woocommerce_process_product_meta action to perform your copy process and can get the sale price from $_POST and validate and format it then you can store it in your custom meta key.
NOTE: I have used your_custom_field_meta_key in the example, you can change it to your desired key name.
/**
* Copy WooCommerce product sales price to custom meta field.
* When a product is created or updated in the backend.
*
* #param int $post_id Post ID.
*
* #return void
*/
function vh_wc_copy_product_sales_price_into_custom_field( $post_id ) {
// Check & Validate the woocommerce meta nonce.
if ( ! ( isset( $_POST['woocommerce_meta_nonce'] ) || wp_verify_nonce( sanitize_key( $_POST['woocommerce_meta_nonce'] ), 'woocommerce_save_data' ) ) ) {
return;
}
$sale_price = isset( $_POST['_sale_price'] ) ? wc_clean( wp_unslash( $_POST['_sale_price'] ) ) : null;
update_post_meta( $post_id, 'your_custom_field_meta_key', wc_format_decimal( $sale_price ) );
}
add_action( 'woocommerce_process_product_meta', 'vh_wc_copy_product_sales_price_into_custom_field', 99 );

I have modified my code with recommendations above and it works well now.
For anyone looking for the solution, here is the code:
/**
* Copy WooCommerce product sales price to custom meta field.
* When a product is created or updated in the backend.
*
* #param int $post_id Post ID.
*
* #return void
*/
function vh_wc_copy_product_sales_price_into_custom_field( $post_id ) {
// Check & Validate the woocommerce meta nonce.
if ( ! ( isset( $_POST['woocommerce_meta_nonce'] ) || wp_verify_nonce( sanitize_key( $_POST['woocommerce_meta_nonce'] ), 'woocommerce_save_data' ) ) ) {
return;
}
$sale_price = isset( $_POST['_price'] ) ? wc_clean( wp_unslash( $_POST['_price'] ) ) : null;
update_post_meta( $post_id, 'codisto_product_price_cad', wc_format_decimal( $sale_price ) );
}
add_action( 'woocommerce_process_product_meta', 'vh_wc_copy_product_sales_price_into_custom_field', 99 );
add_action( 'save_post', 'add_value_on_update_product', 10, 3);
function add_value_on_update_product($post_id, $post, $update){
if ($post->post_status != 'publish' || $post->post_type != 'product') {
return;
}
// Check & Validate the woocommerce meta nonce.
if ( ! ( isset( $_POST['woocommerce_meta_nonce'] ) || wp_verify_nonce( sanitize_key( $_POST['woocommerce_meta_nonce'] ), 'woocommerce_save_data' ) ) ) {
return;
}
if (!$product = wc_get_product( $post )) {
return;
}
$sale_price = isset( $_POST['_canada_sale_price'] ) ? wc_clean( wp_unslash( $_POST['_canada_sale_price'] ) ) : null;
update_post_meta( $post_id, 'codisto_product_price_cad', wc_format_decimal( $sale_price ) );
}

Related

Issue with changing WooCommerce cart item prices based on product meta

When the product subtotal is more than 3000, it has to be another price.
I added it as a meta field to products. On Stack Overflow, a long time ago someone advised me the filter for a special price called woocommerce_cart_item_name
I modified it a little for my purpose:
function kia_add_subtitle_to_cart_product( $title, $cart_item ){
$threshold = 3000; // Change price if > 3000
$_product = $cart_item['data'];
$meta = $_product->get_meta( '_opt_price_var');
$price = $_product->get_regular_price();
$subtotal = $cart_item['quantity'] * $price;
if ( $subtotal > $threshold && ( $meta )) {
$_product->set_price( $meta );
}
echo $title;
}
add_filter( 'woocommerce_cart_item_name', 'kia_add_subtitle_to_cart_product', 10, 2 );
The special price is showing only in the cart form, but not in cart totals and not in the checkout, in totals and in checkout the price is still regular, the main calculator doesn't take it from this filter.
How can I fix it? How to include it in the main calculator too?
I tried different hooks that can be responsive for the calculator - both add_action and add_filter, with and without 10, 2 - it isn't it.
add_action('woocommerce_before_shipping_calculator', 'kia_add_subtitle_to_cart_product',10,2);
add_action('woocommerce_before_cart_totals', 'kia_add_subtitle_to_cart_product', 10, 2 );
The woocommerce_cart_item_name hook is being misused in your code attempt. A filter hook should always return something compared to the use of echo.
You should use the woocommerce_before_calculate_totals hook instead
So you get:
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return;
// Change price if > 3000
$threshold = 3000;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Get regular price
$price = $cart_item['data']->get_regular_price();
// Subtotal = Get quantity * price
$subtotal = $cart_item['quantity'] * $price;
// Greater than
if ( $subtotal > $threshold ) {
// Get meta
$meta = $cart_item['data']->get_meta( '_opt_price_var', true );
// NOT empty
if ( ! empty ( $meta ) ) {
// Set new price
$cart_item['data']->set_price( $meta );
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );
add_filter('woocommerce_calculated_total', 'custom_calculated_total', 10, 2);
function custom_calculated_total($total, $cart_object) {
if (is_admin() && !defined('DOING_AJAX'))
return;
if (!WC()->cart->is_empty()):
foreach ($cart_object->cart_contents as $key => $cart_item) {
$meta = $cart_item['data']->get_meta('_opt_price_var');
if ($total >= 3000 && ( $meta )) {
$total = $meta;
}
}
endif;
return $total;
}

Woocommerce sort by price after set custom discount on product

I added a custom discount in all products via functions.php, because we want to run a campaign based on specific brand.
Prices are showing in product page, product list and cart page.
//Change price in product and product list
add_filter( 'woocommerce_get_price_html', 'set_discount_by_brand', 9999, 2 );
function set_discount_by_brand( $price_html, $product ) {
$discount = 0.25;
$brand = $product->get_attribute('pa_brand');
if ($brand == 'MY BRAND') {
$regular_price = $product->get_regular_price();
$sale_price = $regular_price*(1-$discount);
$price_html = '<del>' . ( is_numeric( $regular_price ) ? wc_price( $regular_price ) : $regular_price ) . '</del> <ins>' . ( is_numeric( $sale_price ) ? wc_price( $sale_price ) : $sale_price ) . '</ins>';
}
return $price_html;
}
//Change price in cart item
add_action( 'woocommerce_before_calculate_totals', 'discount_price_cart_by_brand', 9999 );
function discount_price_cart_by_brand( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return;
// Apply discount to cart
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
$product = $cart_item['data'];
$product_id = $product->get_id();
$discount = 0.25;
$brand = $product->get_attribute('pa_brand');
if ($brand == 'MY BRAND') {
$regular_price = $product->get_regular_price();
$sale_price = $regular_price*(1-$discount);
$cart_item['data']->set_price( $sale_price );
}
}
}
My problem is when i try to sort products by price (asc & desc). I know that sorting based in meta_key _price but i don't want to make any change in database.
I also thought to add a custom meta_key named _sorting_price and calculate again all prices based in discount.
//Sort by custom meta_key
add_filter('woocommerce_get_catalog_ordering_args', function ($args) {
$orderby_value = isset($_GET['orderby']) ? wc_clean($_GET['orderby']) : apply_filters('woocommerce_default_catalog_orderby', get_option('woocommerce_default_catalog_orderby'));
if ('price' == $orderby_value) {
$args['orderby'] = 'meta_value_num';
$args['order'] = 'ASC';
$args['meta_key'] = '_sorting_price';
}
if ('price-desc' == $orderby_value) {
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
$args['meta_key'] = '_sorting_price';
}
return $args;
});
But i would like to try first another option instead to add 10000 rows in postmeta table.
Does anyone know if i can do it via functions.php?
Thank you
Because you are doing this via filter and just adjusting the display price there is no way to do it via built in functions.
You could also use a separate table to do this vs adding more data to the postmeta. This would make it easier to just remove later as you could just delete the table when done.
You could update all the products to be on sale programmatically which would work with the default sort.

Update WooCommerce order status if product custom field is set

I need to automatically set a certain order status (different than processing) when getting a new order.
This is achieved by this function:
add_action('woocommerce_thankyou','change_order_status');
function change_order_status( $order_id ) {
if ( ! $order_id ) { return; }
$order = wc_get_order( $order_id );
if( 'processing'== $order->get_status() ) {
$order->update_status( 'wc-custom-status' );
}
}
This totally works. Now I only need this to happen when a product has a customization.
The way to customize a product is filling an input field before adding to cart. The input is attached to the item data:
// Add custom cart item data
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_cart_item_data', 10, 2 );
function add_custom_cart_item_data( $cart_item_data, $product_id ){
if( isset($_POST['custom_text']) ) {
$cart_item_data['custom_text'] = sanitize_text_field( $_POST['custom_text'] );
$cart_item_data['unique_key'] = md5( microtime().rand() ); // Make each item unique
}
return $cart_item_data;
}
Then the custom text is retrieved and displayed in cart and in the order data using this:
// Display custom cart item data on cart and checkout
add_filter( 'woocommerce_get_item_data', 'display_custom_cart_item_data', 10, 2 );
function display_custom_cart_item_data( $cart_item_data, $cart_item ) {
if ( !empty( $cart_item['custom_text'] ) ){
$cart_item_data[] = array(
'name' => __('Customization', 'woocommerce'),
'value' => $cart_item['custom_text'] // Already sanitized field
);
}
return $cart_item_data;
}
// Save and display custom item data everywhere on orders and email notifications
add_action( 'woocommerce_checkout_create_order_line_item', 'add_product_custom_field_as_order_item_meta', 10, 4 );
function add_product_custom_field_as_order_item_meta( $item, $cart_item_key, $values, $order ) {
if ( isset($values['custom_text']) ) {
$item->update_meta_data('Add on', $values['custom_text'] );
}
}
I'm trying using the if ( isset($values['custom_text']) ) part as a trigger of the function to change the order status only if the product add on is set and other similar methods (like if ( !empty( $cart_item['custom_text'] ) ) but I'm not sure this is the way to go:
add_action('woocommerce_thankyou','change_order_status');
function change_order_status( $order_id ) {
if ( ! $order_id ) {return;}
$order = wc_get_order( $order_id );
if ( isset($values['custom_text']) ) {
if( 'processing'== $order->get_status() ) {
$order->update_status( 'wc-custom-status' );
}
}
}
This above does nothing. Am I anywhere near it with this approach?
EDIT: I tried this too
add_action('woocommerce_thankyou','change_order_status');
function change_order_status( $order_id ) {
if ( ! $order_id ) {return;}
$order = wc_get_order( $order_id );
foreach ( $order->get_items() as $item_id => $item ) {
$allmeta = $item->get_meta_data();
if ( isset($values['custom_text']) ) {
if( 'processing'== $order->get_status() ) {
$order->update_status( 'wc-custom-status' );
}
}
}
}
Your code contains some unnecessary steps as well as some shortcomings
For example, the woocommerce_add_cart_item_data hook contains 3 arguments versus 2
So you get:
// Add custom cart item data
function filter_add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
// Isset and NOT empty
if ( isset ( $_POST[ 'custom_text' ] ) && ! empty ( $_POST[ 'custom_text' ] ) ) {
$cart_item_data['custom_text'] = sanitize_text_field( $_POST['custom_text'] );
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'filter_add_cart_item_data', 10, 3 );
// Display custom cart item data on cart and checkout
function filter_woocommerce_get_item_data( $cart_data, $cart_item = null ) {
if ( isset ( $cart_item['custom_text'] ) ) {
$cart_data[] = array(
'name' => __( 'Customization', 'woocommerce' ),
'value' => $cart_item['custom_text']
);
}
return $cart_data;
}
add_filter( 'woocommerce_get_item_data', 'filter_woocommerce_get_item_data', 10, 2 );
// Add the information as meta data so that it can be seen as part of the order
// Save and display custom item data everywhere on orders and email notifications
function action_woocommerce_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
if ( isset ( $values['custom_text'] ) ) {
$item->update_meta_data( 'custom_text', $values['custom_text'] );
}
}
add_action( 'woocommerce_checkout_create_order_line_item', 'action_woocommerce_checkout_create_order_line_item', 10, 4 );
// On thankyou page
function action_woocommerce_thankyou( $order_id ) {
// Get $order object
$order = wc_get_order( $order_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Only when the current order status is 'processing'
if ( $order->get_status() == 'processing' ) {
// Loop trough
foreach ( $order->get_items() as $item ) {
// Get meta
$value = $item->get_meta( 'custom_text' );
// NOT empty
if ( ! empty ( $value ) ) {
// Update status (change to desired status)
$order->update_status( 'cancelled' );
// Stop loop
break;
}
}
}
}
}
add_action( 'woocommerce_thankyou', 'action_woocommerce_thankyou', 10, 1 );
Currently it is just checking if the value exists, to compare it effectively
Change
// NOT empty
if ( ! empty ( $value ) ) {
// Update status
$order->update_status( 'cancelled' );
// Stop loop
break;
}
To
// Compare
if ( $value == 'some value' ) ) {
// Update status
$order->update_status( 'cancelled' );
// Stop loop
break;
}

Add a filter dropdown for payment method on WooCommerce admin orders list

After reading https://rudrastyh.com/woocommerce/columns.html I was able to add a column with the order payment method on WooCommerce admin orders list
add_filter('manage_edit-shop_order_columns', 'misha_order_items_column' );
function misha_order_items_column( $order_columns ) {
$order_columns['order_payment_method'] = "Payment method";
return $order_columns;
}
add_action( 'manage_shop_order_posts_custom_column' , 'misha_order_items_column_cnt' );
function misha_order_items_column_cnt( $colname ) {
global $the_order; // the global order object
if( $colname == 'order_payment_method' ) {
// Get payment method
$payment_method = $the_order->get_payment_method();
echo $payment_method;
}
}
This works perfectly, and adds the desired statuses in the custom column
Then, based on a code I found, I made adjustments to add the filter
add_action('restrict_manage_posts', 'add_shop_order_filter_by_state');
function add_shop_order_filter_by_state(){
global $pagenow, $typenow, $the_order;
if( 'shop_order' === $typenow && 'edit.php' === $pagenow ) {
// Get payment method
$payment_method = $the_order->get_payment_method();
// Initializing
$filter_id = 'payment_method';
$current = isset($_GET[$filter_id])? $_GET[$filter_id] : '';
echo '<select name="'.$filter_id.'">
<option value="">'.__( 'Filter by payment method', 'woocommerce' )."</option>";
// Loop through shipping zones locations array
foreach( $payment_method as $method ) {
echo $method;
}
echo '</select>';
}
}
add_filter( 'request', 'process_admin_shop_order_filtering_by_state', 99 );
function process_admin_shop_order_filtering_by_state( $vars ) {
global $pagenow, $typenow;
$filter_id = 'payment_method';
if ( $pagenow == 'edit.php' && 'shop_order' === $typenow
&& isset( $_GET[$filter_id] ) && ! empty($_GET[$filter_id]) ) {
$vars['meta_key'] = 'payment_method';
$vars['meta_value'] = $_GET[$filter_id];
$vars['orderby'] = 'meta_value';
}
return $vars;
}
But because of those changes, in the 2nd part of my code I have an error in my log files:
"Uncaught Error: Call to a member function get_payment_method() on
null".
It seems that the global variable $the_order is not recognized?
Although I don't immediately have an idea how I can apply this differently. Any adivce would be appreciated
You are close in the search for your answer, however your code contains some small errors:
Make use of WC_Payment_Gateways::get_available_payment_gateways()
instead of get_payment_method()
The correct meta key is _payment_method versus payment_method
I optimized the code a bit
So you get:
// Display new column on WooCommerce admin orders list (header)
function filter_manage_edit_shop_order_columns( $columns ) {
// Add new column after order status (4) column
return array_slice( $columns, 0, 4, true )
+ array( 'order_payment_method' => __( 'Payment method', 'woocommerce' ) )
+ array_slice( $columns, 4, NULL, true );
}
add_filter( 'manage_edit-shop_order_columns', 'filter_manage_edit_shop_order_columns', 10, 1 );
// Display details after order status column, on order admin list (populate the column)
function action_manage_shop_order_posts_custom_column( $column, $post_id ) {
// Compare
if ( $column == 'order_payment_method' ) {
// Get order
$order = wc_get_order( $post_id );
// Get the payment method
$payment_method = $order->get_payment_method();
// NOT empty
if ( ! empty ( $payment_method ) ) {
echo ucfirst( $payment_method );
} else {
echo __( 'N/A', 'woocommerce' );
}
}
}
add_action( 'manage_shop_order_posts_custom_column' , 'action_manage_shop_order_posts_custom_column', 10, 2 );
// Add filter dropdown
function action_restrict_manage_posts( $post_type, $which ) {
global $pagenow;
// Compare
if ( $post_type === 'shop_order' && $pagenow === 'edit.php' ) {
// Filter ID
$filter_id = 'filter-by-payment';
$current = isset( $_GET[$filter_id] ) ? $_GET[$filter_id] : '';
// Get available gateways
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
// Create a drop-down list
echo '<select name="' . $filter_id . '">
<option value="">' . __( 'Filter by payment method', 'woocommerce' ) . '</option>';
foreach ( $available_gateways as $key => $available_gateway ) {
printf( '<option %s value="%s">%s</option>', $key === $current ? 'selected="selected"' : '', $key, ucfirst( $key ) );
}
echo '</select>';
}
}
add_action( 'restrict_manage_posts', 'action_restrict_manage_posts', 10, 2 );
// Filter request
function filter_request( $vars ) {
global $pagenow, $typenow;
// Filter ID
$filter_id = 'filter-by-payment';
// Only on WooCommerce admin orders list
if ( $pagenow == 'edit.php' && 'shop_order' === $typenow && isset( $_GET[$filter_id] ) && ! empty( $_GET[$filter_id] ) ) {
$vars['meta_key'] = '_payment_method';
$vars['meta_value'] = $_GET[$filter_id];
$vars['orderby'] = 'meta_value';
}
return $vars;
}
add_filter( 'request', 'filter_request', 10, 1 );
Result:

how update post meta when post type post edit and save

hi all here is my function
function save_urun_meta_price( $post_id ) {
$slug = 'urun';
if ( $slug != $_POST['post_type'] ) {
return;
}
// - Update the post's metadata.
if ( isset( $_REQUEST['urun_indirimli_fiyat'] ) ) {
$product_price = get_post_meta( $post_id, 'urun_fiyat', true );
$product_discount = of_get_option('urun_discount');
$yuzde = ($product_discount / 100)*$product_price;
$discounted = $product_price-$yuzde;
update_post_meta( $post_id, 'urun_indirimli_fiyat', $discounted );
}
}
add_action( 'save_post', 'save_urun_meta_price' );
add_action( 'edit_post', 'save_urun_meta_price' );
When user write some price into the urun_fiyat meta field i want to calculate this price with % discount field from the options framework panel.
Than i want to put new price another meta field urun_indirimli_fiyat..
What is wrong with my function ?
Thanks.
Try using the below code. I think the problem was with the product price variable. You were trying to get the value from post meta ( which does not exist )
Getting the value from $_POST variable will do the trick I guess.
function save_urun_meta_price( $post_id ) {
$slug = 'urun';
if ( $slug != $_POST['post_type'] ) {
return;
}
// - Update the post's metadata.
if ( isset( $_REQUEST['urun_indirimli_fiyat'] ) ) {
$product_price = $_POST['urun_fiyat'];
$product_discount = of_get_option('urun_discount');
$yuzde = ($product_discount / 100)*$product_price;
$discounted = $product_price-$yuzde;
update_post_meta( $post_id, 'urun_indirimli_fiyat', $discounted );
}
}
add_action( 'save_post', 'save_urun_meta_price' );
add_action( 'edit_post', 'save_urun_meta_price' );

Resources