Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save shameemreza/6813e4f6bae3981b6674e9185bf4318c to your computer and use it in GitHub Desktop.

Select an option

Save shameemreza/6813e4f6bae3981b6674e9185bf4318c to your computer and use it in GitHub Desktop.
One-time admin-side script to fix incorrect tax calculations in WooCommerce Subscriptions. Visit each affected subscription in the admin panel to trigger the recalculation. Safe to remove after use.
/**
* Simple one-time fix for WooCommerce Subscription tax calculation.
*
* This function automatically recalculates taxes for subscriptions when viewing them in admin.
* Add this code to your theme's functions.php or a custom plugin, visit each affected
* subscription once, then remove the code.
*
* @since 1.0.0
*/
function hafwpv_fix_subscription_taxes() {
// Only run in admin
if ( ! is_admin() ) {
return;
}
// Check if we're on a subscription edit page
global $pagenow, $post_type;
if ( 'post.php' !== $pagenow && 'admin.php' !== $pagenow ) {
return;
}
// Get subscription ID from URL
$subscription_id = 0;
if ( isset( $_GET['post'] ) ) {
$subscription_id = absint( $_GET['post'] );
} elseif ( isset( $_GET['id'] ) ) {
$subscription_id = absint( $_GET['id'] );
}
if ( ! $subscription_id ) {
return;
}
// Check if this is a subscription
$subscription = wcs_get_subscription( $subscription_id );
if ( ! $subscription ) {
return;
}
// Log that we're fixing this subscription
wc_get_logger()->info(
sprintf( 'Fixing tax calculation for subscription #%d', $subscription_id ),
array( 'source' => 'subscription-tax-fix' )
);
// Force recalculation of taxes for all line items
foreach ( $subscription->get_items() as $item_id => $item ) {
$product = $item->get_product();
if ( ! $product ) {
continue;
}
// Get the correct tax class from the product
$tax_class = $product->get_tax_class();
// Update the item's tax class
$item->set_tax_class( $tax_class );
$item->save();
}
// Recalculate taxes and totals
$subscription->calculate_taxes();
$subscription->calculate_totals();
$subscription->save();
// Add a note to the subscription
$subscription->add_order_note( esc_html__( 'Tax calculation fixed by admin script.', 'hafwpv' ) );
// Log completion
wc_get_logger()->info(
sprintf( 'Tax calculation fixed for subscription #%d', $subscription_id ),
array( 'source' => 'subscription-tax-fix' )
);
}
add_action( 'admin_init', 'hafwpv_fix_subscription_taxes' );
@shameemreza
Copy link
Author

/**
 * WooCommerce Subscriptions Tax Fix
 * Fixes tax calculation issues where renewal orders are created with untaxed amounts
 * instead of the correct tax-inclusive amounts.
 * 
 * Add this code to your theme's functions.php file or create a custom plugin.
 * Version 2.0
 */

// Ensure correct tax calculation when renewal orders are created.
add_action( 'wcs_renewal_order_created', 'fix_renewal_order_taxes', 15, 2 );

/**
 * Fix taxes on renewal order creation.
 *
 * @param WC_Order        $renewal_order The renewal order.
 * @param WC_Subscription $subscription  The subscription.
 */
function fix_renewal_order_taxes( $renewal_order, $subscription ) {
	$needs_update = false;
	
	foreach ( $renewal_order->get_items() as $item ) {
		if ( ! $item->is_type( 'line_item' ) ) {
			continue;
		}
		
		$product = $item->get_product();
		if ( ! $product ) {
			continue;
		}
		
		// Get the correct tax class from the product.
		$product_tax_class = $product->get_tax_class();
		$item_tax_class    = $item->get_tax_class();
		
		// Update if tax class is different.
		if ( $product_tax_class !== $item_tax_class ) {
			$item->set_tax_class( $product_tax_class );
			$item->save();
			$needs_update = true;
		}
	}
	
	// If we made changes, recalculate the order.
	if ( $needs_update ) {
		$renewal_order->calculate_taxes();
		$renewal_order->calculate_totals( false );
		$renewal_order->save();
		
		// Add order note.
		$renewal_order->add_order_note( 
			__( 'Tax calculation automatically corrected during renewal creation.', 'woocommerce-subscriptions' ) 
		);
	}
}

// Fix subscription items before renewal order is created.
add_filter( 'wcs_renewal_order_items', 'fix_renewal_items_tax_calculation', 5, 3 );

/**
 * Fix tax calculations on renewal items.
 *
 * @param array           $items         Items to be added to renewal order.
 * @param WC_Order        $renewal_order The renewal order.
 * @param WC_Subscription $subscription  The subscription.
 * @return array Modified items.
 */
function fix_renewal_items_tax_calculation( $items, $renewal_order, $subscription ) {
	foreach ( $items as $item_key => $item ) {
		if ( ! $item->is_type( 'line_item' ) ) {
			continue;
		}
		
		$product = $item->get_product();
		if ( ! $product ) {
			continue;
		}
		
		// Ensure tax class matches the product.
		$product_tax_class = $product->get_tax_class();
		$item->set_tax_class( $product_tax_class );
		
		// Get tax rates for proper calculation.
		$tax_rates = WC_Tax::get_rates( $product_tax_class );
		
		if ( ! empty( $tax_rates ) ) {
			$subtotal = $item->get_subtotal();
			$total    = $item->get_total();
			
			// Check if prices include tax.
			$prices_include_tax = $subscription->get_prices_include_tax();
			
			if ( $prices_include_tax ) {
				// Calculate inclusive tax.
				$subtotal_taxes = WC_Tax::calc_inclusive_tax( $subtotal, $tax_rates );
				$total_taxes    = WC_Tax::calc_inclusive_tax( $total, $tax_rates );
			} else {
				// Calculate exclusive tax.
				$subtotal_taxes = WC_Tax::calc_exclusive_tax( $subtotal, $tax_rates );
				$total_taxes    = WC_Tax::calc_exclusive_tax( $total, $tax_rates );
			}
			
			// Set the calculated taxes.
			$item->set_taxes( array(
				'subtotal' => $subtotal_taxes,
				'total'    => $total_taxes,
			) );
		}
	}
	
	return $items;
}

// One-time fix for existing subscriptions when viewing in admin.
add_action( 'load-post.php', 'fix_subscription_taxes_on_admin_view' );

/**
 * Fix subscription taxes when viewing in admin.
 */
function fix_subscription_taxes_on_admin_view() {
	// Only run in admin when viewing/editing.
	if ( ! is_admin() ) {
		return;
	}
	
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading URL parameter for display only.
	$post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0;
	
	if ( ! $post_id ) {
		return;
	}
	
	// Check if it's a subscription.
	if ( get_post_type( $post_id ) !== 'shop_subscription' ) {
		return;
	}
	
	$subscription = wcs_get_subscription( $post_id );
	if ( ! $subscription ) {
		return;
	}
	
	// Check if we've already fixed this subscription recently (within last day).
	$last_fixed = get_post_meta( $post_id, '_tax_fix_applied', true );
	if ( $last_fixed && ( time() - $last_fixed ) < DAY_IN_SECONDS ) {
		return;
	}
	
	$fixes_applied = false;
	
	// Fix each line item.
	foreach ( $subscription->get_items() as $item ) {
		if ( ! $item->is_type( 'line_item' ) ) {
			continue;
		}
		
		$product = $item->get_product();
		if ( ! $product ) {
			continue;
		}
		
		// Get correct tax class from product.
		$product_tax_class = $product->get_tax_class();
		$current_tax_class = $item->get_tax_class();
		
		if ( $product_tax_class !== $current_tax_class ) {
			$item->set_tax_class( $product_tax_class );
			$item->save();
			$fixes_applied = true;
		}
	}
	
	if ( $fixes_applied ) {
		// Recalculate subscription totals.
		$subscription->calculate_taxes();
		$subscription->calculate_totals( false );
		$subscription->save();
		
		// Update timestamp.
		update_post_meta( $post_id, '_tax_fix_applied', time() );
		
		// Add admin note.
		$subscription->add_order_note( 
			__( 'Tax classes updated to match current product settings.', 'woocommerce-subscriptions' ) 
		);
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment