Automattic\WooCommerce\Admin\API\Reports\Products
DataStore::sync_order_products │ public static │ WC 3.5.0
Create or update an entry in the wc_admin_order_product_lookup table for an order.
Метод класса: DataStore{}
Хуки из метода
Возвращает
int|true|false. Returns -1 if order won't be processed, or a boolean indicating processing success.
Использование
$result = DataStore::sync_order_products( $order_id );
- $order_id(int) (обязательный)
- Order ID.
Список изменений
| С версии 3.5.0 | Введена. |
Код DataStore::sync_order_products() DataStore::sync order products WC 10.5.2
public static function sync_order_products( $order_id ) {
global $wpdb;
$order = wc_get_order( $order_id );
if ( ! $order ) {
return -1;
}
$table_name = self::get_db_table_name();
$existing_items = $wpdb->get_col(
$wpdb->prepare(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"SELECT order_item_id FROM {$table_name} WHERE order_id = %d",
$order_id
)
);
$existing_items = array_flip( $existing_items );
$order_items = $order->get_items();
$num_updated = 0;
$decimals = wc_get_price_decimals();
$round_tax = 'no' === get_option( 'woocommerce_tax_round_at_subtotal' );
$is_full_refund_without_line_items = false;
$partial_refund_product_revenue = array();
$refund_type = $order->get_meta( '_refund_type' );
$uses_new_full_refund_data = OrderUtil::uses_new_full_refund_data();
$parent_order = null;
// When changing the order status to "Refunded", the refund order's type will be full refund, and the order items will be empty.
// We need to get the parent order items, and exclude the items that are already being partially refunded.
if (
'shop_order_refund' === $order->get_type() &&
'full' === $refund_type &&
empty( $order_items ) &&
$uses_new_full_refund_data
) {
$is_full_refund_without_line_items = true;
$parent_order_id = $order->get_parent_id();
$parent_order = wc_get_order( $parent_order_id );
$order_items = $parent_order->get_items();
// Get the partially refunded product and variation IDs along with their sum of product_net_revenue from the parent order.
$partial_refund_products = $wpdb->get_results(
$wpdb->prepare(
"
SELECT
product_lookup.product_id,
product_lookup.variation_id,
SUM( product_lookup.product_net_revenue ) AS product_net_revenue
FROM %i AS product_lookup
INNER JOIN {$wpdb->prefix}wc_order_stats AS order_stats
ON order_stats.order_id = product_lookup.order_id
WHERE 1 = 1
AND order_stats.parent_id = %d
AND product_lookup.product_net_revenue < 0
GROUP BY product_lookup.product_id, product_lookup.variation_id
",
$table_name,
$parent_order_id
)
);
/**
* Create a lookup table for partially refunded products.
* E.g. [
* '1' => -20,
* '2' => -40,
* '51' => -10,
* '52' => -30,
* ]
*/
foreach ( $partial_refund_products as $product ) {
$id = $product->variation_id ? $product->variation_id : $product->product_id;
$partial_refund_product_revenue[ $id ] = (float) $product->product_net_revenue;
}
}
foreach ( $order_items as $order_item ) {
$order_item_id = $order_item->get_id();
unset( $existing_items[ $order_item_id ] );
$product_qty = $order_item->get_quantity( 'edit' );
$product_id = $order_item->get_product_id( 'edit' );
$variation_id = $order_item->get_variation_id( 'edit' );
$shipping_amount = $order->get_item_shipping_amount( $order_item );
$shipping_tax_amount = $order->get_item_shipping_tax_amount( $order_item );
$coupon_amount = $order->get_item_coupon_amount( $order_item );
$tax_amount = $order->get_item_cart_tax_amount( $order_item );
$net_revenue = round( $order_item->get_total( 'edit' ), $decimals );
// If the order is a full refund and there is no order items. The order item here is the parent order item.
if ( $is_full_refund_without_line_items ) {
$id = $variation_id ? $variation_id : $product_id;
$partial_refund = $partial_refund_product_revenue[ $id ] ?? 0;
// If a single line item was refunded 60% then fully refunded after, we need store the difference in the product lookup table.
// E.g. A product costs $100, it was previously partially refunded $60, then fully refunded $40.
// So it will be -abs( 100 + (-60) ) = -40.
$net_revenue = -abs( $net_revenue + $partial_refund );
// Skip items that have already been fully refunded (single or multiple partial refunds).
if ( 0.0 === $net_revenue ) {
continue;
}
$product_qty = -abs( $product_qty );
// Set coupon amount to 0 for full refunds without line items.
$coupon_amount = 0;
if ( $parent_order ) {
$remaining_refund_items = $parent_order->get_remaining_refund_items();
// Calculate the shipping amount to refund from the parent order.
$total_shipping_refunded = $parent_order->get_total_shipping_refunded();
$shipping_total = (float) $parent_order->get_shipping_total();
$total_shipping_to_refund = $shipping_total - $total_shipping_refunded;
if ( $total_shipping_to_refund > 0 ) {
$shipping_amount = -abs( $parent_order->get_item_shipping_amount( $order_item, $remaining_refund_items, $total_shipping_to_refund ) );
}
// Calculate the shipping tax amount to refund from the parent order.
$shipping_tax = (float) $parent_order->get_shipping_tax();
$total_shipping_tax_refunded = $parent_order->get_total_shipping_tax_refunded();
$total_shipping_tax_to_refund = $shipping_tax - $total_shipping_tax_refunded;
if ( $total_shipping_tax_to_refund > 0 ) {
$shipping_tax_amount = -abs( $parent_order->get_item_shipping_tax_amount( $order_item, $remaining_refund_items, $total_shipping_tax_to_refund ) );
}
// Calculate cart tax amount of the item from the parent order.
$tax_amount = -abs( $parent_order->get_item_cart_tax_amount( $order_item ) );
}
}
$is_refund = $net_revenue < 0;
// Skip line items without changes to product quantity.
if ( ! $product_qty && ! $is_refund ) {
++$num_updated;
continue;
}
if ( $round_tax ) {
$tax_amount = round( $tax_amount, $decimals );
}
$result = $wpdb->replace(
self::get_db_table_name(),
array(
'order_item_id' => $order_item_id,
'order_id' => $order->get_id(),
'product_id' => $product_id,
'variation_id' => $variation_id,
'customer_id' => $order->get_report_customer_id(),
'product_qty' => $product_qty,
'product_net_revenue' => $net_revenue,
'date_created' => $order->get_date_created( 'edit' )->date( TimeInterval::$sql_datetime_format ),
'coupon_amount' => $coupon_amount,
'tax_amount' => $tax_amount,
'shipping_amount' => $shipping_amount,
'shipping_tax_amount' => $shipping_tax_amount,
// @todo Can this be incorrect if modified by filters?
'product_gross_revenue' => $net_revenue + $tax_amount + $shipping_amount + $shipping_tax_amount,
),
array(
'%d', // order_item_id.
'%d', // order_id.
'%d', // product_id.
'%d', // variation_id.
'%d', // customer_id.
'%d', // product_qty.
'%f', // product_net_revenue.
'%s', // date_created.
'%f', // coupon_amount.
'%f', // tax_amount.
'%f', // shipping_amount.
'%f', // shipping_tax_amount.
'%f', // product_gross_revenue.
)
); // WPCS: cache ok, DB call ok, unprepared SQL ok.
/**
* Fires when product's reports are updated.
*
* @param int $order_item_id Order Item ID.
* @param int $order_id Order ID.
*/
do_action( 'woocommerce_analytics_update_product', $order_item_id, $order->get_id() );
// Sum the rows affected. Using REPLACE can affect 2 rows if the row already exists.
$num_updated += 2 === intval( $result ) ? 1 : intval( $result );
}
if ( ! empty( $existing_items ) ) {
$existing_items = array_flip( $existing_items );
$format = array_fill( 0, count( $existing_items ), '%d' );
$format = implode( ',', $format );
array_unshift( $existing_items, $order_id );
$wpdb->query(
$wpdb->prepare(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"DELETE FROM {$table_name} WHERE order_id = %d AND order_item_id in ({$format})",
$existing_items
)
);
}
return ( count( $order_items ) === $num_updated );
}