<?php
/**
* Tax Rate IDs fixer
*
* @package Stripe\StripeTaxForWooCommerce\Stripe
*/
namespace Stripe\StripeTaxForWooCommerce\Stripe;
defined( 'ABSPATH' ) || exit;
use Stripe\StripeTaxForWooCommerce\WooCommerce\StripeTaxTaxRateMemRepo;
use Exception;
/**
* StringTaxRateIdFixer class.
*/
class StringTaxRateIdFixer {
const CACHE_GROUP = 'stripe-tax-for-woocommerce-order-finder';
const CACHE_KEY = 'tax-woocommerce-order-finder';
const NUM_ORDERS = 50;
/**
* Returns $orders
*/
public static function get_orders() {
$wc_order_ids = self::get_order_ids();
if ( count( $wc_order_ids ) === 0 ) {
return array();
}
$orders = array();
foreach ( $wc_order_ids as $wc_order_id ) {
$wc_order = wc_get_order( $wc_order_id );
if ( ! $wc_order ) {
continue;
}
$orders[ $wc_order_id ] = $wc_order;
}
return $orders;
}
/**
* Returns WooCommerce order IDs that have at least one **order line item**
* whose `_line_tax_data` meta contains the substring "stripe_tax_for_woocommerce".
*
* Safety:
* - Table names are built from `$wpdb->prefix` + known suffixes (no user input).
* - Only values are passed through `$wpdb->prepare()`.
*
* @return int[] List of order IDs (cast to int).
*/
private static function get_order_ids(): array {
$items = wp_cache_get( self::CACHE_KEY, self::CACHE_GROUP );
if ( is_array( $items ) ) {
wp_cache_set( self::CACHE_KEY, $items, self::CACHE_GROUP, 2 * HOUR_IN_SECONDS );
}
global $wpdb;
$like = '%' . $wpdb->esc_like( 'stripe_tax_for_woocommerce' ) . '%';
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
$results = $wpdb->get_col(
$wpdb->prepare(
"SELECT DISTINCT woi.order_id
FROM {$wpdb->prefix}woocommerce_order_items AS woi
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim
ON woim.order_item_id = woi.order_item_id
WHERE woi.order_item_type = 'line_item'
AND woim.meta_key = %s
AND meta_value LIKE %s
LIMIT %d",
array(
'_line_tax_data',
$like,
static::NUM_ORDERS,
)
)
);
return array_map( 'intval', $results );
}
/**
* Converts an order string tax rate IDs to integers.
*
* @param object $order Order.
*/
public static function fix_order_tax_rate_ids( $order ) {
$fixed_rate_id_map = array();
$tax_items = $order->get_items( 'tax' );
foreach ( array( 'line_item', 'shipping' ) as $line_item_type ) {
$items = $order->get_items( $line_item_type );
foreach ( $items as $item ) {
$item_taxes = $item->get_taxes();
$fixed_item_taxes = array();
foreach ( array( 'total', 'subtotal' ) as $total_key ) {
if ( ! array_key_exists( $total_key, $item_taxes ) || ! is_array( $item_taxes[ $total_key ] ) ) {
continue;
}
foreach ( $item_taxes[ $total_key ] as $rate_id => $tax_amount ) {
$rate_id_parts = explode( '__', $rate_id );
if ( count( $rate_id_parts ) < 4 ) {
continue;
}
$fixed_rate_id = StripeTaxTaxRateMemRepo::find_or_create(
'',
'',
$rate_id_parts[2],
$rate_id_parts[3]
);
$fixed_item_taxes[ $total_key ][ $fixed_rate_id ] = $tax_amount;
$fixed_rate_id_map[ $rate_id ] = $fixed_rate_id;
}
}
$item->set_taxes( $fixed_item_taxes );
$item->save();
}
}
$tax_items = $order->get_items( 'tax' );
foreach ( $tax_items as $tax_item ) {
$rate_id = $tax_item->get_rate_id();
$rate_code = $tax_item->get_rate_code();
$fixed_rate_id = null;
if ( isset( $fixed_rate_id_map [ $rate_id ] ) ) {
$fixed_rate_id = $fixed_rate_id_map [ $rate_id ];
} elseif ( isset( $fixed_rate_id_map [ $rate_code ] ) ) {
$fixed_rate_id = $fixed_rate_id_map [ $rate_code ];
}
if ( is_null( $fixed_rate_id ) ) {
continue;
}
$tax_item->set_rate_id( $fixed_rate_id );
$tax_item->save();
}
}
/**
* Process he next NUM_ORDERS orders.
*/
public static function process() {
$orders = static::get_orders();
foreach ( $orders as $order ) {
static::fix_order_tax_rate_ids( $order );
}
return count( $orders );
}
}