I have completed a woocommerce website for a client. As per the client requirement I got an issue. My client wants their agents just need to able to search for the orders but they should not able to edit any of the order or they should not see any other option. Agents should see all the order details like phone number, email and complete billing address but they should not able to edit anything.
I have tried few user role editors none of them giving that option.
Note : All the orders are guest orders only!
Please let me know if you have any idea how to achieve this.
Try this in your theme functions.php
//mydomain.com/my-account/view-order/xxxx - any order number here
function my_customer_has_capability( $allcaps, $caps, $args ) {
if ( isset( $caps[0] ) ) {
switch ( $caps[0] ) {
case 'view_order' :
$user_id = $args[1];
$order = wc_get_order( $args[2] );
if ( $order && $user_id == $order->user_id || my_get_current_user_role() == 'editor') {
$allcaps['view_order'] = true;
}
break;
}
}
return $allcaps;
}
add_filter( 'user_has_cap', 'my_customer_has_capability', 10, 3 );
function my_get_current_user_role() {
$current_user = wp_get_current_user();
if ( !($current_user instanceof WP_User) )
return;
if( empty($current_user->roles[0]) )
return false;
$role = ($current_user->roles[0]);
return (string)$role;
}
Related
The intention is represented on the attached Image. Essentially to display on WooCommerce admin backend order page list. If the order comes from a new Customer (if it is his first order) so that admins can know it's a persons first order on the store.
I have figured out that I should be able to check if it is a first order with the following snippet.
function new_customer_warning( $document_type, $order )
{
if( !empty($order) && $document_type == 'invoice' ) {
if( $order->get_user_id() != 0 ) {
$customer_orders = wc_get_customer_order_count( $order->get_user_id() );
if( $customer_orders == 1 ) {
echo '<tr><th><strong>New customer!</strong></th><td></td></tr>';
}
}
}
}
However I can't figure out a hook for the admin Orders page, for how to display the intended New Order warning there.
As an alternative (or ideally having it in both places) The "New Customer" message could be echoed directly inside of the Order Details page.
This image shows an example of this concept on both locations:
You could use the manage_shop_order_posts_custom_column and woocommerce_admin_order_data_after_order_details action hook
// Admin order list
function action_manage_shop_order_posts_custom_column( $column, $post_id ) {
// Compare
if ( $column == 'order_number' ) {
// Get order
$order = wc_get_order( $post_id );
// Getting the user ID
$user_id = $order->get_user_id();
// User ID exists
if ( $user_id >= 1 ) {
// Get the user order count
$order_count = wc_get_customer_order_count( $user_id );
// First order
if ( $order_count == 1 ) {
echo ' <span>– ' . __( 'New customer!', 'woocommerce' ) . '</span>';
}
}
}
}
add_action( 'manage_shop_order_posts_custom_column', 'action_manage_shop_order_posts_custom_column', 20, 2 );
// Order details page
function action_woocommerce_admin_order_data_after_order_details( $order ) {
// Getting the user ID
$user_id = $order->get_user_id();
// User ID exists
if ( $user_id >= 1 ) {
// Get the user order count
$order_count = wc_get_customer_order_count( $user_id );
// First order
if ( $order_count == 1 ) {
echo '<h3>' . __( 'New customer!', 'woocommerce' ) . '</h3>';
}
}
}
add_action( 'woocommerce_admin_order_data_after_order_details', 'action_woocommerce_admin_order_data_after_order_details', 10, 1 );
I would like to prevent (make these fields readonly, for example) users from changing their billing information on the WooCommerce checkout form.
I'm currently using this code snippet:
add_filter('woocommerce_billing_fields', 'mycustom_woocommerce_billing_fields', 10, 1 );
function mycustom_woocommerce_billing_fields($fields)
{
$fields['billing_first_name']['custom_attributes'] = array('readonly'=>'readonly');
$fields['billing_last_name']['custom_attributes'] = array('readonly'=>'readonly');
$fields['billing_email']['custom_attributes'] = array('readonly'=>'readonly');
$fields['billing_phone']['custom_attributes'] = array('readonly'=>'readonly');
return $fields;
}
But the problem is: If the user has not filled in any of these fields in the registration, he is unable to insert his data in the checkout form because these fields are not editable.
My question is:
If the fields are not empty, how to make them readonly (or disabled)
Someone who can help me with this?
The answer of 7uc1f3r certainly works getting the user data… But since WooCommerce 3, you can use WC_Checkout get_value() dedicated method as follow:
add_filter( 'woocommerce_billing_fields', 'filter_wc_billing_fields', 10, 1 );
function filter_wc_billing_fields( $fields ) {
// On checkout and if user is logged in
if ( is_checkout() && is_user_logged_in() ) {
// Define your key fields below
$keys_fields = ['billing_first_name', 'billing_last_name', 'billing_email', 'billing_phone'];
// Loop through your specific defined fields
foreach ( $keys_fields as $key ) {
// Check that a value exist for the current field
if( ( $value = WC()->checkout->get_value($key) ) && ! empty( $value ) ) {
// Make it readonly if a value exist
$fields[$key]['custom_attributes'] = ['readonly'=>'readonly'];
}
}
}
return $fields;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
If you want this code to be also active in My account > Addresses > edit…, you will just have to remove is_checkout() && from the first IF statement.
I believe this is what you are looking for, comment with explanation added in code
function mycustom_woocommerce_billing_fields( $fields ) {
// Get current user
$user = wp_get_current_user();
// User id
$user_id = $user->ID;
// User id is found
if ( $user_id > 0 ) {
// Fields
$read_only_fields = array ( 'billing_first_name', 'billing_last_name', 'billing_email', 'billing_phone' );
// Loop
foreach ( $fields as $key => $field ) {
if( in_array( $key, $read_only_fields ) ) {
// Get key value
$key_value = get_user_meta($user_id, $key, true);
if( strlen( $key_value ) > 0 ) {
$fields[$key]['custom_attributes'] = array(
'readonly'=>'readonly'
);
}
}
}
}
return $fields;
}
add_filter('woocommerce_billing_fields', 'mycustom_woocommerce_billing_fields', 10, 1 );
I need help with a custom email hook for woocommerce.
I am trying to send a different email depending on product ID whenever a product is completed.
My code, which is not working, is as follows:
/**************
DIFFERENT MESSAGES FOR DIFFERENT PRODUCTS
****************/
//hook our function to the new order email
add_action('woocommerce_email_order_details', 'uiwc_email_order_details_products', 1, 4);
function uiwc_email_order_details_products($order, $admin, $plain, $email) {
$status = $order->get_status();
// checking if it's the order status we want
if ( $status == "completed" ) {
$items = $order->get_items();
if ( $item['product_id'] == "3181") {
echo __( '<strong>IMPORTANT - NEXT STEP:</strong><br>To get started, please follow this link to complete the Policies form.<br><br>This is a really important first step, and only takes about 5 minutes. After completeing the Policies form, you will receive additional instructions on next steps.<br><br>Congratulations! Let your journey begin.<br><br>', 'uiwc' );
}
elseif ( $item['product_id'] == "3223") {
echo __( '<strong>IMPORTANT - NEXT STEP:</strong><br>Differnet product so differenct email....<br><br>', 'uiwc' );
}
}
}
Any suggestions is greatly appreciated
There is some mistakes in your code, instead try the following
//hook our function to the new order email
add_action( 'woocommerce_email_order_details', 'custom_email_order_details', 4, 4 );
function custom_email_order_details( $order, $admin, $plain, $email ) {
$domain = 'woocommerce';
// checking if it's the order status we want
if ( $order->has_status('completed') ) {
foreach( $order->get_items() as $item ){
if ( $item->get_product_id() == '3181' ) {
echo __( '<strong>IMPORTANT - NEXT STEP:</strong><br>To get started, please follow this link to complete the Policies form.<br><br>This is a really important first step, and only takes about 5 minutes. After completeing the Policies form, you will receive additional instructions on next steps.<br><br>Congratulations! Let your journey begin.<br><br>', $domain );
break;
}
elseif ( $item->get_product_id() == '3223' ) {
echo __( '<strong>IMPORTANT - NEXT STEP:</strong><br>Differnet product so differenct email....<br><br>', $domain );
break;
}
}
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I need to change item product prices in Woocommerce Backend Order. I tried tu use the following hook, but I have a problem trying to obtain the order id. Any suggestion? Thanks in advance!
function my_custom_prices ($price, $product)
{
if ( !is_admin() || ( is_admin() && is_post_type_archive() ) ) return $price;
global $post, $woocommerce;
$order = new WC_Order( $post_id );
$user_id = $order->user_id;
$user = get_user_by('id', $user_id);
if ( in_array( 'my_role', (array) $user->roles ) ) {
return $price * 2;
}
else {
return $price;
}
}
add_filter('woocommerce_get_price', 'my_custom_prices ', 10, 2);
Complete problem:
The complete problem is as follows. I am using a plugin that adds a field to the product called wholesale price. If the customer has the wholesale customer role, the order uses those prices. The plugin works fine, but it does not take the price in the backend. I talked to the author and it's something they do not plan to change yet. My client needs to modify the orders. But when it enters the backend, it takes the common price, not the wholesaler. I need to do something in the backend that allows me to detect if the order is from a client with a wholesale customer role. If yes, take the correct price when adding products. There is more information on the discussion with the author here. https://wordpress.org/support/topic/wholesale-prices-in-backend-editing-orders/ Thank you very much for the help you can give me.
Options:
woocommerce_get_price: does not work because I cannot obtain the customer id
woocommerce_ajax_add_order_item_meta: nice option, but I could not find a sample
Button: nice option, but I do not know how can I change the price. I tryed the follow:
add_action( 'woocommerce_order_item_add_action_buttons', 'action_aplicar_mayoristas', 10, 1);
function action_aplicar_mayoristas( $order )
{
echo '<button type="button" onclick="document.post.submit();" class="button button-primary generate-items">Aplicar precios mayoristas</button>';
echo '<input type="hidden" value="1" name="aplicar_mayoristas" />';
};
add_action('save_post', 'aplicar_mayoristas', 10, 3);
function aplicar_mayoristas($post_id, $post, $update){
$slug = 'shop_order';
if(is_admin()){
if ( $slug != $post->post_type ) {
return;
}
if(isset($_POST['aplicar_mayoristas']) && $_POST['aplicar_mayoristas']){
$order = wc_get_order( $post_id);
//$order_id = $order->get_user_id();
// Iterating through each "line" items in the order
foreach ($order->get_items() as $item_id => $item_data) {
//$item_data->set_subtotal("798");
$item_data->set_price("798");
//->set_price($custom_price);
}
}
}
}
Updated
The hook that you are using is not for orders, but only for products, and is made to change the displayed prices only. So you will not get the order ID with it.
You could change the price display in many hooks, but if you want to change order item prices for real (not only the displayed formatted prices), you should trigger this prices changes when order is updated for example.
In this case you can use a custom function hooked in save_post action hook:
add_action( 'save_post', 'change_order_item_prices', 11, 1 );
function change_order_item_prices( $post_id ) {
// If this is an autosave (has not been submitted).
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return $post_id;
// Check the user's permissions.
if ( 'shop_order' == $_POST[ 'post_type' ] ){
if ( ! current_user_can( 'edit_shop_order', $post_id ) )
return $post_id;
} else {
if ( ! current_user_can( 'edit_post', $post_id ) )
return $post_id;
}
## ------------------- Changing prices Start code ------------------- ##
## ===> HERE define the targeted user role
$user_role = 'my_role';
## ===> HERE define the rate multiplier (for price calculations)
$multiplier = 2;
// If this Order items prices have already been updated, we exit
$items_prices_updated = get_post_meta( $post_id, 'line_item_updated', true );
if( ! empty( $items_prices_updated ) ) return $post_id; // Exit
$order = new WC_Order( $post_id ); // Get the order object
$user_id = $order->get_user_id(); // Get the user ID
$user_data = get_userdata( $user_id ); // Get the user data
// Check the user role
if ( ! in_array( $user_role, $user_data->roles ) ) return;
// Loop through order items
foreach( $order->get_items() as $item_id => $item ){
$item_data = $item->get_data(); // The item data
$taxes = array();
foreach( $item_data['taxes'] as $key_tax => $values ){
if( ! empty( $values ) ){
foreach( $values as $key => $tax_price ){
$taxes[$key_tax][$key] = floatval($tax_price) * $multiplier;
}
}
}
$new_line_subtotal = floatval( $item_data['subtotal'] ) * $multiplier;
$new_line_subt_tax = floatval( $item_data['subtotal_tax'] ) * $multiplier;
$new_line_total = floatval( $item_data['total'] ) * $multiplier;
$new_line_total_tax = floatval( $item_data['total_tax'] ) * $multiplier;
// Update Order item prices
$item->set_subtotal($new_line_subtotal);
$item->set_subtotal_tax($new_line_subt_tax);
$item->set_total($new_line_total);
$item->set_total_tax($new_line_total_tax);
$item->set_taxes($taxes);
// Save the updated data
$item->save();
}
// Udpate order totals and cache
$order->calculate_totals();
// We mark the order as updated (to avoid repetition)
update_post_meta( $post_id, 'line_item_updated', true );
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested and and finally works.
I have added a security to avoid the order items to be updated twice.
The method $order->calculate_totals(); slow down the process a little bit… It's normal as it will calculate totals, update data and refresh caches.
Your code needs to be debugged.
$post_id - there is not such variable or parameter inside your function. So, use $post->ID instead.
do
var_dump( (array) $user->roles);
before the
if (in_array( 'my_role', (array) $user->roles ) )
line. And make sure that my_role exists in that array.
Temporary comment this line for debugging purpose:
// if (is_admin() && is_post_type_archive())
Then you will see the reason and able to be fix it.
I want to add column to show the customer role on the WooCommerce orders, I search and everything I found is for one user. I also found this code on this link (WooCommerce show custom column) but I do not understand where I put what I need.
I also found this code (https://gist.github.com/corsonr/5975207)
but I do not managed to get the user role.
I added $user_role = $user->roles;
and
switch ($column)
{
case "user_role":
echo $user_role;
break;
}
but it not worked, I know it is array but use of [0] or [1] did not work.
I missing something?
It is possible to do what I what?
Add below code in your theme functions.php
add_filter('manage_edit-shop_order_columns', 'add_column_heading', 20, 1);
function add_column_heading($array) {
$res = array_slice($array, 0, 2, true) +
array("customer_role" => "Customer Role") +
array_slice($array, 2, count($array) - 1, true);
return $res;
}
add_action('manage_posts_custom_column', 'add_column_data', 20, 2);
function add_column_data($column_key, $order_id) {
// exit early if this is not the column we want
if ('customer_role' != $column_key) {
return;
}
$customer = new WC_Order( $order_id );
if($customer->user_id != ''){
$user = new WP_User( $customer->user_id );
if ( !empty( $user->roles ) && is_array( $user->roles ) ) {
foreach ( $user->roles as $role )
echo $role;
}
}
}