{ return; } // Retrieve customer details before the order is deleted. $order = wc_get_order( $order_id ); $customer_id = absint( CustomersDataStore::get_existing_customer_id_from_order( $order ) ); // Delete the order. $wpdb->delete( self::get_db_table_name(), array( 'order_id' => $order_id ) ); /** * Fires when orders stats are deleted. * * @param int $order_id Order ID. * @param int $customer_id Customer ID. * * @since 4.0.0 */ do_action( 'woocommerce_analytics_delete_order_stats', $order_id, $customer_id ); ReportsCache::invalidate(); } /** * Calculation methods. */ /** * Get number of items sold among all orders. * * @param WC_Order|WC_Order_Refund $order WC_Order or WC_Order_Refund object. * @return int */ protected static function get_num_items_sold( $order ) { $num_items = 0; $line_items = $order->get_items( OrderItemType::LINE_ITEM ); foreach ( $line_items as $line_item ) { $num_items += $line_item->get_quantity(); } return $num_items; } /** * Get the net amount from an order without shipping, tax, or refunds. * * @param WC_Order|WC_Order_Refund $order WC_Order or WC_Order_Refund object. * @return float */ protected static function get_net_total( $order ) { $net_total = floatval( $order->get_total() ) - floatval( $order->get_total_tax() ) - floatval( $order->get_shipping_total() ); return (float) $net_total; } /** * Whether this refund is a single lump-sum refund for the full order (e.g. status set to refunded without line items). * * @param WC_Order_Refund|OrderRefund|Order $refund Refund order. * @param WC_Order|Order $parent_order Parent order (not a refund). * @return bool */ protected static function should_split_full_refund_using_parent_order( $refund, $parent_order ) { // The parent must be the original order, not another refund. if ( ! $parent_order instanceof WC_Order || 'shop_order_refund' === $parent_order->get_type() ) { return false; } if ( self::get_num_items_sold( $refund ) > 0 ) { return false; } $parent_refunds = $parent_order->get_refunds(); if ( 1 !== count( $parent_refunds ) ) { return false; } $refund_total = wc_format_decimal( abs( (float) $refund->get_total() ) ); $order_total = wc_format_decimal( (float) $parent_order->get_total() ); return $refund_total === $order_total; } /** * Check if the wc_order_stats table has the fulfillment_status column. * * @return boolean */ public static function has_fulfillment_status_column() { $column_status = get_option( self::OPTION_ORDER_STATS_TABLE_HAS_COLUMN_ORDER_FULFILLMENT_STATUS ); if ( ! empty( $column_status ) ) { return 'yes' === $column_status; } global $wpdb; $table_name = self::get_db_table_name(); // Check if the table exists. $table_exists = $wpdb->get_var( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name cannot be prepared. 'SHOW TABLES LIKE %s', $table_name ) ); // If table still does not exist, return false without setting the option to allow for table to be created with the column. if ( ! $table_exists ) { return false; } $column_exists = $wpdb->get_var( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name cannot be prepared. "SHOW COLUMNS FROM `{$table_name}` LIKE %s", 'fulfillment_status' ) ); if ( ! empty( $column_exists ) ) { update_option( self::OPTION_ORDER_STATS_TABLE_HAS_COLUMN_ORDER_FULFILLMENT_STATUS, 'yes', false ); return true; } // Update the option to indicate that the column does not exist. update_option( self::OPTION_ORDER_STATS_TABLE_HAS_COLUMN_ORDER_FULFILLMENT_STATUS, 'no', false ); return false; } /** * Check to see if an order's customer has made previous orders or not * * @param WC_Order $order WC_Order object. * @param int|false $customer_id Customer ID. Optional. * @return bool */ public static function is_returning_customer( $order, $customer_id = null ) { if ( is_null( $customer_id ) ) { $customer_id = \Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore::get_existing_customer_id_from_order( $order ); } if ( ! $customer_id ) { return false; } $oldest_orders = \Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore::get_oldest_orders( $customer_id ); if ( empty( $oldest_orders ) ) { return false; } $first_order = $oldest_orders[0]; $second_order = isset( $oldest_orders[1] ) ? $oldest_orders[1] : false; $excluded_statuses = self::get_excluded_report_order_statuses(); // Order is older than previous first order. if ( $order->get_date_created() < wc_string_to_datetime( $first_order->date_created ) && ! in_array( $order->get_status(), $excluded_statuses, true ) ) { self::set_customer_first_order( $customer_id, $order->get_id() ); return false; } // The current order is the oldest known order. $is_first_order = (int) $order->get_id() === (int) $first_order->order_id; // Order date has changed and next oldest is now the first order. $date_change = $second_order && $order->get_date_created() > wc_string_to_datetime( $first_order->date_created ) && wc_string_to_datetime( $second_order->date_created ) < $order->get_date_created(); // Status has changed to an excluded status and next oldest order is now the first order. $status_change = $second_order && in_array( $order->get_status(), $excluded_statuses, true ); if ( $is_first_order && ( $date_change || $status_change ) ) { self::set_customer_first_order( $customer_id, $second_order->order_id ); return true; } return (int) $order->get_id() !== (int) $first_order->order_id; } /** * Set a customer's first order and all others to returning. * * @param int $customer_id Customer ID. * @param int $order_id Order ID. */ protected static function set_customer_first_order( $customer_id, $order_id ) { global $wpdb; $orders_stats_table = self::get_db_table_name(); $wpdb->query( $wpdb->prepare( 'UPDATE %i SET returning_customer = CASE WHEN order_id = %d THEN false ELSE true END WHERE customer_id = %d', $orders_stats_table, $order_id, $customer_id ) ); } /** * Add fulfillment_status column to wc_order_stats table. * * @return bool|string True on success, error message string on failure. */ public static function add_fulfillment_status_column() { if ( self::has_fulfillment_status_column() ) { return true; } global $wpdb; $result = $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching "ALTER TABLE {$wpdb->prefix}wc_order_stats ADD COLUMN fulfillment_status VARCHAR(50) DEFAULT NULL, ADD INDEX fulfillment_status (fulfillment_status)" ); if ( false === $result ) { return $wpdb->last_error ? $wpdb->last_error : __( 'Unknown database error occurred while adding fulfillment_status column.', 'woocommerce' ); } // Update the option to indicate that the column has been added. update_option( self::OPTION_ORDER_STATS_TABLE_HAS_COLUMN_ORDER_FULFILLMENT_STATUS, 'yes', false ); return true; } }