Disable Cancel Subscription for single subscription in WooCommerce - wordpress

I have 3 subscriptions available on my WooCommerce site. 1 of the subscription plans is Annual Paid Monthly. This essential means a minimum term of 12 month but paid month to month. WooCommerce doesn't natively support this.
What I would like to do is detect if the user is on a particular subscription and if so, hide the cancel button until the subscription is greater than 11 months.
I found this below that hides the cancel button in all cases. I'm looking for a way to check if the subscription is XXX and if so, hide the cancel button instead
/**
* Only copy the opening php tag if needed
*/
function sv_edit_my_memberships_actions( $actions ) {
// remove the "Cancel" action for members
unset( $actions['cancel'] );
return $actions;
}
add_filter( 'wc_memberships_members_area_my-memberships_actions', 'sv_edit_my_memberships_actions' );
add_filter( 'wc_memberships_members_area_my-membership-details_actions', 'sv_edit_my_memberships_actions' );

First of you need to enumerate the users current active subscriptions and detect the one we shall filter using wc_memberships_get_user_active_memberships and compare the starting date with the current date. I provided a snippet of code that might help you on the way :)
function sv_edit_my_memberships_actions( $actions ) {
// Get the current active user
$user_id = wp_get_current_user();
if(!$user_id) // No valid user, abort
return $actions;
// Only query active subscriptions
$memberships_info = wc_memberships_get_user_active_memberships($user_id, array(
'status' => array( 'active' ),
));
// Loop through each active subscription
foreach ($memberships_info as $membership) {
$subscription_start_date = date("Y/m/d", strtotime($membership->get_start_date()));
//$subscription_end_date = date("Y/m/d", strtotime($membership->get_end_date()));
//$subscription_name = $membership->get_plan()->get_name();
//$subscription_id = $membership->get_plan()->get_id();
if($subscription_id == 'YOUR_ID') { // Active subscription
// Compare the starting date of the subscription with the current date
$datetime1 = date_create($subscription_start_date);
$datetime2 = date_create(date(time()));
$interval = date_diff($datetime1, $datetime2);
if($interval->format('%m') <= 11) {
// remove the "Cancel" action for members
unset( $actions['cancel'] );
}
}
}
return $actions;
}
add_filter( 'wc_memberships_members_area_my-memberships_actions', 'sv_edit_my_memberships_actions' );
add_filter( 'wc_memberships_members_area_my-membership-details_actions', 'sv_edit_my_memberships_actions' );

Related

Which hook needs to call if admin update the shipping/billing address in WooCommerce?

I'm not sure which hook/action needs to setup to know when the admin is updating shipping/billing addresses once the order has been created.
So what I'm trying to achieve here is:
In WooCommerce order section when the admin updates the shipping/billing address then it triggers an action.
this action basically makes a single curl call to my custom script and lets me know that the address of the order has been changed by the admin.
I'll do some magic in my script.
I found below but I don't think its more from admin side.
// define the woocommerce_admin_order_data_after_shipping_address callback
function action_woocommerce_admin_order_data_after_shipping_address(
$delta_wccs_custom_checkout_details_pro_shipping, $int, $int ) {
// make action magic happen here...
};
// add the action
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 3 );
Please let me know if anyone knows the right action to trigger when order shipping/billing address change.
The woocommerce_admin_order_data_after_shipping_address hook is to display extra content on the order edit page (backend)
To trigger $order_item actions before or after saving to the DB, use:
/**
* Trigger action before saving to the DB. Allows you to adjust object props before save.
*
* #param WC_Data $this The object being saved.
* #param WC_Data_Store_WP $data_store THe data store persisting the data.
*/
function action_woocommerce_before_order_item_object_save( $order_item, $data_store ) {
// Get type
$data_type = $order_item->get_type();
// Before billing changes
if ( $data_type == 'billing' ) {
// Do..
}
// Before shipping changes
if ( $data_type == 'shipping' ) {
// Do..
}
}
add_action( 'woocommerce_before_order_item_object_save', 'action_woocommerce_before_order_item_object_save', 10, 2 );
/**
* Trigger action after saving to the DB.
*
* #param WC_Data $this The object being saved.
* #param WC_Data_Store_WP $data_store THe data store persisting the data.
*/
function action_woocommerce_after_order_item_object_save( $order_item, $data_store ) {
// Get type
$data_type = $order_item->get_type();
// After billing changes
if ( $data_type == 'billing' ) {
// Do..
}
// After shipping changes
if ( $data_type == 'shipping' ) {
// Do..
}
}
add_action( 'woocommerce_after_order_item_object_save', 'action_woocommerce_after_order_item_object_save', 10, 2 );
OR
Use the almost identical woocommerce_before_order_object_save hook that may be even more suitable, because via $order->get_changes() you can trigger/log/compare which $order data has been changed
function action_woocommerce_before_order_object_save( $order, $data_store ) {
// Get changes
$changes = $order->get_changes();
// Billing OR shipping
if ( isset( $changes['billing'] ) || isset( $changes['shipping'] ) ) {
// Do..
}
// OR even more specific (e.g.: shipping first name field was changed)
if ( isset( $changes['shipping_first_name'] ) ) {
// Do..
}
}
add_action( 'woocommerce_before_order_object_save', 'action_woocommerce_before_order_object_save', 10, 2 );
EDIT: it is a known issue that these hooks are called multiple times when they are not intended to be
See: https://github.com/woocommerce/woocommerce/issues/25771
As a workaround, add:
if ( did_action( 'replace_by_the_desired_hook_name' ) >= 2 ) return;
As first line in your callback function
// Define the woocommerce_admin_order_data_after_shipping_address callback .
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// This hook will only fire in backend when viewing the order edit screen. Not for orders placed from checkout
};
// add the action
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );

How to copy an order item meta from the parent item to the child order item in a Woocommerce Subscription?

I'm running a function hooked to woocommerce_order_status_completed that uses an API to get a phone number and pin from an external source and save that in a meta key on some order items. Some of these order items are subscriptions.
With the current code, when a Woocommerce subscription renewal order runs automatically, it fires the API and gets a new set of call-in data, but I want to stop it from doing that.
I need to check if a completed order is a subscription renewal and if so, skip the API call and instead get the renewed item's parent meta data and insert it into the that child items meta.
The top portion of the code I have tried here is not working. The API call portion of the code within else{} is working so I have truncated it.
add_action ( 'woocommerce_order_status_completed', 'add_item_meta_or_run_api', 10 , 1);
function add_item_meta_or_run_api( $order_id ) {
$order = wc_get_order( $order_id );
// if (wcs_order_contains_subscription( $order, 'renewal' )){ //check if the order contains a renewal subscription
if (wcs_order_contains_renewal( $order_id)){ //Updated: a better way to do this.
foreach ($order->get_items() as $item_id => $item_obj) { //loop through each rewnewal item
$parent_id = $item_obj->get_parent_id(); // Get the parent order ID for the subscriptions.
$parentSubscriptions = wcs_get_subscriptions_for_order( $parent_id );//get parent order subscriptions
foreach ( $parentSubscriptions->get_items() as $parent_item_id => $subscription_item_obj) { //loop through parent order items and get the meta.
$ParentCallinData = $subscription_item_obj->get_meta('call_in_data');
// Store parenent item call in data in renewal order item meta
wc_update_order_item_meta($item_id,'call_in_data', $ParentCallinData, true);
}
}
}
else {//if there is not a subscription renewal in the order then we run the API
foreach ($order->get_items() as $item_id => $item_obj) {
//Code here has been removed that builds and runs the API call to dynamically get the call-in data and store it in $APIresponse
wc_update_order_item_meta($item_id,'call_in_data', $APIresponse, true); //the APIresponse is added to an order item meta key. I need to insert this meta in each child subscription.
}
}
}

Trigger woocommerce_payment_complete when creating order manually

I have a hook to woocommerce_payment_complete, in which I send the order to the distributor. This is working fine.
Now, since I'm also selling through 3rd party marketplace, sometimes I want to create an order form the admin panel, and I expect the woocommerce_payment_complete hook to be triggered by setting the order status to 'Processing' but it's not.
Is there any way to trigger this hook by creating an order manually?
Thanks
You can use the following to set 'processing' for admin orders. action_woocommerce_process_shop_order_meta is used to detect the order update.
// define the woocommerce_admin_order_actions_end callback
function action_woocommerce_admin_order_actions_end( $order_id ) {
global $woocommerce;
if (!$order_id)
return;
$order = new WC_Order($order_id);
$order_status = $order->get_status();
if ($order_status != "failed") {
$order->update_status('processing');
}
};
// add the action
add_action( 'action_woocommerce_process_shop_order_meta', 'action_woocommerce_admin_order_actions_end', 10, 1 );

Woocommerce "No Shipping methods" message: Customise message based on the Zipcode customer enters

Our store has been setup to process orders only within Sydney city. We manage this in Woocommerce by setting the allowed postcodes for a Flat Rate Delivery.
We are now extending deliveries to other other areas but only via telephone (no online orders for these new postcodes). Woocommerce displays a standard No Shipping message but we would to customise such that
Customer enters a postcode within the city, allow online orders. No
change to current behaviour.
Customer enters a postcode for which
telephone orders are allowed, show a customised message asking the
customer to make the call.
All other postcodes, disallow orders.
Any technical direction will be greatly appreciated.
Thanks.
Below code should help you. Change the value of array variable $zip_array in both the functions as a comma separated list of zip codes, which you want to show a custom message. Also, change the string value of $custom_msg to your custom message. More details, please refer this article.
// For Cart Page.
add_filter( 'woocommerce_no_shipping_available_html', 'wf_customize_default_message', 10, 1 );
// For Checkout page
add_filter( 'woocommerce_cart_no_shipping_available_html', 'wf_customize_default_message', 10, 1 );
function wf_customize_default_message( $default_msg ) {
$zip_array = array(
'30031',
);
if ( in_array( WC()->customer->get_shipping_postcode() , $zip_array) ) {
$custom_msg = "Call us for quotation - 1-800-XXX-XXXX";
if( empty( $custom_msg ) ) {
return $default_msg;
}
return $custom_msg;
}
return $default_msg;
}
add_filter('woocommerce_package_rates', 'wf_remove_shipping_options_for_particular_zip_codes', 8, 2);
function wf_remove_shipping_options_for_particular_zip_codes($rates, $package)
{
global $woocommerce;
$zip_array = array(
'30031',
);
if ( in_array( $woocommerce->customer->get_shipping_postcode() , $zip_array) ) {
$rates = array();
}
return $rates;
}

Applying a coupon code programmatically to the cart/order

I've been stuck trying to apply a Drupal commerce coupon for about 2 days now. I have taken care of validating the coupon and currently stumped when I'm trying to redeem it.
So inside my callback function I'm calling:
my_module_coupons_coupon_redeem($coupon);
And inside the redeem function I have:
function my_module_coupons_coupon_redeem($coupon) {
global $user;
$uid = $user->uid;
$order = commerce_cart_order_load($uid);
// Wrap the order for easy access to field data.
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
// Create the new line item.
$line_item = commerce_coupon_line_item_new($coupon, $order->order_id);
$line_item->commerce_unit_price = array('und' => array(
'0' => array('amount' => 500, 'currency_code' => commerce_default_currency())
));
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
if (!is_null($line_item_wrapper->commerce_unit_price->value())) {
// Add the base price to the components array.
if (!commerce_price_component_load($line_item_wrapper->commerce_unit_price->value(), 'base_price')) {
$line_item_wrapper->commerce_unit_price->data = commerce_price_component_add(
$line_item_wrapper->commerce_unit_price->value(),
'base_price',
$line_item_wrapper->commerce_unit_price->value(),
TRUE
);
}
}
$line_item_wrapper->commerce_total->data = $line_item_wrapper->commerce_unit_price->data;
//$line_item_wrapper->commerce_product->data = $coupon;
// Save the line item now so we get its ID.
commerce_line_item_save($line_item);
// Add it to the order's line item reference value.
$order_wrapper->commerce_line_items[] = $line_item;
commerce_order_save($order);
}
The coupon line_item is being saved on the DB but when I refresh the cart page I get the following error:
EntityMetadataWrapperException: Unknown data property commerce_product. in EntityStructureWrapper->getPropertyInfo()
Just wondering if this is the correct way of applying coupons without the use of Rules and should I be saving it as a line item in the first place?
Take a look in sites/all/modules/commerce_coupon/oncludes/commerce_coupon.checkout_pane.inc
global $user;
$uid = $user->uid;
$error = '';
// load the cart
$order = commerce_cart_order_load($uid);
// load the coupon from the coupon code
// see MySQL -> your-schema, table: commerce_coupon, column: code
$coupon = commerce_coupon_redeem_coupon_code($code, $order, $error);
// 302 to the cart
drupal_goto('checkout/' . $order->order_id);
I realized this was posted a few years ago, but there was no working answer and I still couldn't find a complete solution for it anywhere.
After you've added the line item you still need to add the commerce coupon reference to the order like so:
$order_wrapper->commerce_coupon_order_reference[] = $coupon;
This way it won't get removed when commerce_coupon_commerce_cart_order_refresh() runs.

Resources