Use Case:

Office Manager (Shared) Cart

Overview

In this use case, we’ll look at a scenario where an office wants multiple users to be able to add items to a single cart. Then an office manager can access that cart, make edits and checkout from that cart.

Requirements

  1. Customers have their own login but belong to a group (office) with a shared cart.
  2. Customers can see items in the cart/ and add items to the cart.
  3. Customers can remove their own items from the cart, but cannot modify other users’ items.
  4. The Office Manager can change or remove anyone’s cart items
  5. The Office Manager is the only user that can apply coupons
  6. The Office Manager is the only user that can proceed to checkout and purchase the cart.

Assumptions

We’ll assume we’ve already set up the following functions in our custom plugin:

(stuff we’ll glaze over to get to the Woo stuff)

/**
 * Determines if user is an "office user" role
 * @param  int  $user_id if null, uses get_current_user_id();
 * @return boolean
 */
function cwpdev_user_is_office_user($user_id=null)

/**
 * Get's the user_id of an office_user's office_manage for their group
 * @param  [type] $user_id [description]
 * @return [type]          [description]
 */
function cwpdev_get_office_manager_id($user_id)

1) Viewing an Office Cart

The shared office cart will be a “persistent” cart. There are two user_meta_fields we’ll need to control to ensure this is loaded into the Office User’s cart:

  • ‘_woocommerce_load_saved_cart_after_login’
  • ‘_woocommerce_persistent_cart’.get_current_blog_id();

We’ll use the WordPress core filters to control behavior on these meta fields.

apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, $single );

apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $prev_value );

apply_filters( "delete_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );

1) Viewing an Office Cart (cont)

First, let’s ensure that office users always load the persistent cart on each WP run.

function cwpdev_office_users_load_cart($null, $object_id, $meta_key, $single){
  if( cwpdev_user_is_office_user($object_id)  && $meta_key === '_woocommerce_load_saved_cart_after_login'){
    return true;
  }
  return $null;
}
add_filter('get_user_metadata', 'cwpdev_office_users_load_cart', 15, 4);

Next, We’ll ensure the cart that office users load is always the one that belongs to their office manager.

function cwpdev_office_user_shared_cart($null, $object_id, $meta_key, $single){
  if ( cwpdev_is_office_user($object_id) && $meta_key === '_woocommerce_persistent_cart_'.get_current_blog_id() ){
    $user_manager_id=cwpdev_get_office_manager_id(get_current_user_id());
    return get_user_meta($user_manager_id, '_woocommerce_persistent_cart_'.get_current_blog_id(), true);
  }
  return $null;
}

add_filter('get_user_metadata', 'cwpdev_office_user_shared_cart', 15, 4);

2) Add items to cart

In order to control which cart items a user can remove, we’ll need to designate an owner for each cart item when they add that item. We’ll use the ‘woocommerce_add_cart_item’ to accomplish this.

function cwpdev_mark_cart_item_owner($cart_item_array, $cart_item_key){
  $cart_item_array['owner']=get_current_user_id();
  return $cart_item_array;
}

add_filter('woocommerce_add_cart_item', 'cwpdev_mark_cart_item_owner', 15, 2);

2) Add items to cart (cont)

Next we’ll need to ensure that, when the cart is updated, it actually updates the office manager’s cart.

function cwpdev_update_office_user_shared_cart($null, $object_id, $meta_key, $meta_value, $prev_value){

    if ( cwpdev_is_office_user($object_id) && $meta_key === '_woocommerce_persistent_cart_'.get_current_blog_id() ){
      $user_manager_id=cwpdev_get_office_manager_id(get_current_user_id());

      return update_user_meta($user_manager_id, '_woocommerce_persistent_cart_');
    }

    return $null;
}

add_filter('update_user_metadata', 'cwpdev_update_office_user_shared_cart', 15, 5);

3) Protect cart items

In order to control which cart items a user can remove, we’ll need to designate an owner for each cart item. We’ll use the ‘woocommerce_add_cart_item’ to accomplish this.

function cwpdev_mark_cart_item_owner($cart_item_array, $cart_item_key){
  $cart_item_array['owner']=get_current_user_id();
  return $cart_item_array;
}

add_filter('woocommerce_add_cart_item', 'cwpdev_mark_cart_item_owner', 15, 2);

Programmatically, its fairly easy to prevent an office user from removing an item that isn’t theirs from the cart:

function cwpdev_block_office_user_remove($cart_item_key, $cart){
  if(! cwpdev_user_is_office_user() ){
    return;
  }
  $cart_item_owner=$cart->get_cart_item($cart_item_key)['owner'] ?? -1;
  if((int) $cart_item_owner !== get_current_user_id() ){
    throw new Exception(__("You cannot remove or change another user's items from the cart") );
  }
}

add_action('woocommerce_remove_cart_item', 'cwpdev_block_office_user_remove', 15, 2);

However, that’s not a great user experience…If a user sees a remove from cart link–it should work!

3) Protect Cart items (cont)

To have a user experience that makes sense, we’ll remove the remove cart links, where they do not apply:

function cwpdev_remove_remove_link_from_cart($link, $cart_item_key){
  if(! cwpdev_user_is_office_user() ){
    return $link;
  }
  $cart_item_owner=WC()->cart->get_cart_item($cart_item_key)["owner"] ?? -1;
  if((int) $cart_item_owner !== get_current_user_id() ){
    return '';
  }
  return $link;
}

add_filter('woocommerce_cart_item_remove_link', 'cwpdev_remove_remove_link_from_cart', 15, 2);

4) Office Manager control

We’ve made this easy for ourselves, by adding the cwpdev_is_office_user() guard statements at the top of each of our hooks, office managers can expect normal cart behavior 🙂

5) Only Office Managers can apply coupons

Super easy:

function cwpdev_no_office_user_coupons($bool){
  if( cwpdev_is_office_user()){
    return false;
  }
  return $bool;
}

add_filter('woocommerce_coupons_enabled', 'cwpdev_no_office_user_coupons');

6) Only Office Managers can checkout

This is one that a little simpler to do by targetting templates and the payment gateways.

In your proceed-to-checkout-button template:

<?php if ( ! cwpdev_is_office_user() ): ?>

}
<a href="<?php echo esc_url( wc_get_checkout_url() ); ?>" class="checkout-button button alt wc-forward">
	<?php esc_html_e( 'Proceed to checkout', 'woocommerce' ); ?>
</a>

<?php endif; ?>
function cwpdev_no_paygateway_for_office_user($gateways){
  if( cwpdev_user_is_office_user() ){
    return [];
  }
  return $gateways;
}

add_filter('woocommerce_available_payment_gateways', 'cwpdev_no_paygateway_for_office_user');

And block office users from payment gateways:

Conclusion

So we’ve managed to do a fairly comprehensive customization by staying within the boundaries of standard WooCommerce and WordPress filters.

 

So, when encountering a project that requires careful customization of the way WooCommerce progresses from users selections to building an order, review the flow charts and information in the previous topics. You will likely find an abundance of hooks and options available at your disposal 🙂

 

 That’s all for this lesson, feel free to come back and search the materialRemember, CTRL+Shift+F can be used to do a word search in any of the topics! any time!

References

×

Keyboard shortcuts

CTRL+Shift+F Search slideshow
F Fullscreen view
CTRL+Click Zoom in
Esc Topic overview
Right arrow,
Down arrow
Next slide
Left arrow,
Up arrow
Previous slide

Color codes

Tooltip
Hover over text more additional info
Link to an external resource
Link to an internal slide
If buttons aren't working, click in the screen to "focus" your browser
RSS
LinkedIn
Share