JezK
Edit File: class-cartflows-pro-orders.php
<?php /** * CartFlows Orders * * @package CartFlows * @since 1.0.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Initialization * * @since 1.0.0 */ class Cartflows_Pro_Orders { /** * Member Variable * * @var instance */ private static $instance; /** * Initiator */ public static function get_instance() { if ( ! isset( self::$instance ) ) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ public function __construct() { /* Register New Order Status */ add_filter( 'woocommerce_register_shop_order_post_statuses', array( $this, 'register_new_order_status' ), 99 ); /* Add order Status to WooCommerce options */ add_filter( 'wc_order_statuses', array( $this, 'update_to_native_stauses' ), 99 ); add_action( 'carflows_schedule_normalize_order_status', array( $this, 'schedule_normalize_order_status' ), 99, 3 ); if ( ! wcf_pro()->utils->is_separate_offer_order() ) { /* Only for merged order Order Status to main order */ add_action( 'cartflows_order_started', array( $this, 'register_order_status_to_main_order' ), 10 ); } } /** * Get order status slug. * * @since 1.0.0 * * @return string */ public function get_order_status_slug() { return 'wc-wcf-main-order'; } /** * Get order status title. * * @since 1.0.0 * * @return string */ public function get_order_status_title() { return _x( 'Main Order Accepted (CF)', 'Order status', 'cartflows-pro' ); } /** * Register new order status. * * @since 1.0.0 * @param string $order_status order status. * * @return array */ public function register_new_order_status( $order_status ) { $order_status_title = $this->get_order_status_title(); $order_status[ $this->get_order_status_slug() ] = array( 'label' => $order_status_title, 'public' => false, 'exclude_from_search' => true, 'show_in_admin_all_list' => true, 'show_in_admin_status_list' => true, /* translators: %s: Single count value */ 'label_count' => _n_noop( 'Main Order Accepted <span class="count">(%s)</span>', 'Main Order Accepted <span class="count">(%s)</span>', 'cartflows-pro' ), ); return $order_status; } /** * Update native statuses. * * @since 1.0.0 * @param string $order_status Order status. * * @return array */ public function update_to_native_stauses( $order_status ) { $order_status[ $this->get_order_status_slug() ] = $this->get_order_status_title(); return $order_status; } /** * Add upsell product and order meta. * * @since 1.0.0 * @param array $order order. * @param array $upsell_product upsell product. * @return void */ public function add_upsell_product( $order, $upsell_product ) { wcf()->logger->log( 'Start-' . __CLASS__ . '::' . __FUNCTION__ ); $this->add_offer_product( $order, $upsell_product, 'upsell' ); wcf()->logger->log( 'End-' . __CLASS__ . '::' . __FUNCTION__ ); } /** * Add downsell product. * * @since 1.0.0 * @param array $order order. * @param array $downsell_product downsell product. * @return void */ public function add_downsell_product( $order, $downsell_product ) { $this->add_offer_product( $order, $downsell_product, 'downsell' ); } /** * Add shipping to order. * * @param array $product_data offer product. * @param array $order parent order. * @return void */ public function add_shipping_charges_to_order( $product_data, $order ) { // Add the shipping charges. $item = new WC_Order_Item_Shipping(); $item->set_props( array( 'method_title' => 'Flat rate', 'method_id' => '', 'total' => $product_data['shipping_fee'], ) ); $item->save(); $order->add_item( $item ); // Show product details in shipping rates section of order data. $product_name = $product_data['name'] . ' × ' . $product_data['qty']; $offer_shipping_items = array( $product_name ); $item_id = $item->get_id(); $offer_itmes = implode( ',', $offer_shipping_items ); wc_add_order_item_meta( $item_id, 'Items', $offer_itmes ); $order->calculate_totals(); $order->save(); } /** * Add offer product. * * @since 1.0.0 * @param array $order order. * @param array $product_data offer product. * @param string $type offer product type. * @return void */ public function add_offer_product( $order, $product_data, $type = 'upsell' ) { wcf()->logger->log( 'Start-' . __CLASS__ . '::' . __FUNCTION__ ); wcf()->logger->log( 'Offer type - ' . $type ); $transaction_id = $order->get_meta( 'cartflows_offer_txn_resp_' . $product_data['step_id'] ); $child_order = null; if ( ! wcf_pro()->utils->is_separate_offer_order() ) { $item_id = $order->add_product( wc_get_product( $product_data['id'] ), $product_data['qty'], $product_data['args'] ); /** * This filter 'cartflows_display_offer_shipping_fee' will be true always. * Can be used to hide/show the shipping fee on thank you page depending on the product selected. */ if ( 0 < $product_data['shipping_fee'] || apply_filters( 'cartflows_display_offer_shipping_fee', false, $product_data ) ) { $this->add_shipping_charges_to_order( $product_data, $order ); wc_add_order_item_meta( $item_id, '_cartflows_offer_shipping_fee', $product_data['shipping_fee_tax'] ); } wc_add_order_item_meta( $item_id, '_cartflows_' . $type, 'yes' ); wc_add_order_item_meta( $item_id, '_cartflows_step_id', $product_data['step_id'] ); wc_add_order_item_meta( $item_id, '_cartflows_offer_txn_id', $transaction_id ); $order->calculate_totals(); } else { // Set transaction_id in product data. $product_data['transaction_id'] = $transaction_id; $child_order = $this->create_child_order( $order, $product_data, $type ); } do_action( 'cartflows_offer_product_processed', $order, $product_data, $child_order ); wcf()->logger->log( 'End-' . __CLASS__ . '::' . __FUNCTION__ ); } /** * Normalize order status. * * @since 1.0.0 * @param WC_Order $order order. * @return void */ public function may_be_normalize_status( $order ) { wcf()->logger->log( 'Entering: ' . __CLASS__ . '::' . __FUNCTION__ ); wcf()->logger->log( 'Order status: ' . $order->get_status() ); /* @todo : Check if it is our status */ $flow_id = wcf()->utils->get_flow_id_from_order( $order ); $before_normal = 'pending'; $normal_status = 'processing'; /* Get status change data from order */ $order_status_change_data = $order->get_meta( '_cartflows_order_status_change' ); if ( is_array( $order_status_change_data ) && isset( $order_status_change_data['before_normal'] ) && isset( $order_status_change_data['normal_status'] ) ) { $before_normal = $order_status_change_data['before_normal']; $normal_status = $order_status_change_data['normal_status']; } else { $session_data = wcf_pro()->session->get_data( $flow_id ); if ( $session_data ) { $before_normal = isset( $session_data['before_normal'] ) ? $session_data['before_normal'] : $before_normal; $normal_status = isset( $session_data['normal_status'] ) ? $session_data['normal_status'] : $normal_status; } } $this->do_normalize_status( $order, $before_normal, $normal_status ); } /** * Normalize order status. * * @since 1.0.0 * @param WC_Order $order order. * @param string $before_normal before status. * @param string $normal_status normal status. * @return void */ public function do_normalize_status( $order, $before_normal = 'pending', $normal_status = 'processing' ) { wcf()->logger->log( 'Entering: ' . __CLASS__ . '::' . __FUNCTION__ ); wcf()->logger->log( 'Before Normal: ' . $before_normal ); wcf()->logger->log( 'Normal: ' . $normal_status ); wcf()->logger->log( 'order data: ' . $order ); if ( false === is_a( $order, 'WC_Order' ) ) { return; } $current_status = $order->get_status(); if ( 'wcf-main-order' !== $current_status ) { return; } /* Setup Beofore Normal Status */ $order->update_status( $before_normal ); $normal_status = apply_filters( 'wcf_order_status_after_order_complete', $normal_status, $order ); /* Setup Normal Staus */ $order->update_status( $normal_status ); } /** * Schedule normalize order status. * * @since 1.0.0 * @param int $order_id order id. * @param string $before_normal before status. * @param string $normal_status normal status. * @return void */ public function schedule_normalize_order_status( $order_id, $before_normal, $normal_status ) { $order = wc_get_order( $order_id ); $this->do_normalize_status( $order, $before_normal, $normal_status ); } /** * Register order status to main order. * * @since 1.0.0 * @param array $order order data. * @return void */ public function register_order_status_to_main_order( $order ) { if ( ! is_a( $order, 'WC_Order' ) ) { return; } $payment_method = $order->get_payment_method(); if ( 'cod' === $payment_method || 'bacs' === $payment_method ) { return; } add_filter( 'woocommerce_payment_complete_order_status', array( $this, 'maybe_set_completed_order_status' ), 999, 3 ); } /** * Set order status to complete. * * @since 1.0.0 * @param string $order_status order status. * @param int $id order id. * @param array $order order data. * @return string */ public function maybe_set_completed_order_status( $order_status, $id, $order ) { wcf()->logger->log( __CLASS__ . '::maybe_set_completed_order_status' ); if ( ! is_a( $order, 'WC_Order' ) ) { return $order_status; } remove_filter( 'woocommerce_payment_complete_order_status', array( $this, 'maybe_set_completed_order_status' ), 999 ); $new_status = $this->get_order_status_slug(); /** * $new_status = our new status * $order_status = default status change */ do_action( 'cartflows_order_status_change_to_main_order', $new_status, $order_status, $order ); return $this->get_order_status_slug(); } /** * Create child offer order. * * @since 1.0.0 * @param object $parent_order order. * @param array $product_data offer product. * @param string $type offer product type. */ public function create_child_order( $parent_order, $product_data, $type = 'upsell' ) { wcf()->logger->log( 'Start-' . __CLASS__ . '::' . __FUNCTION__ ); $order = false; if ( ! empty( $parent_order ) ) { $parent_order_id = $parent_order->get_id(); $parent_order_currency = $parent_order->get_currency(); $parent_order_billing = $parent_order->get_address( 'billing' ); $flow_id = $parent_order->get_meta( '_wcf_flow_id' ); if ( apply_filters( 'cartflows_force_create_child_order', ! empty( $parent_order_billing['email'] ), $parent_order ) ) { $customer_id = $parent_order->get_customer_id(); $order = wc_create_order( array( 'customer_id' => $customer_id, 'status' => 'wc-pending', ) ); /* Set Order type */ $order->update_meta_data( '_cartflows_offer', 'yes' ); $order->update_meta_data( '_cartflows_offer_type', $type ); $order->update_meta_data( '_cartflows_parent_flow_id', $flow_id ); $order->update_meta_data( '_cartflows_offer_step_id', $product_data['step_id'] ); $order->update_meta_data( '_cartflows_offer_parent_id', $parent_order_id ); $order->update_meta_data( '_wc_order_attribution_utm_source', '(direct)' ); $order->update_meta_data( '_wc_order_attribution_source_type', 'typein' ); $order->set_currency( $parent_order_currency ); $item_id = $order->add_product( wc_get_product( $product_data['id'] ), $product_data['qty'], $product_data['args'] ); if ( 0 < $product_data['shipping_fee'] || apply_filters( 'cartflows_display_offer_shipping_fee', false, $product_data ) ) { $this->add_shipping_charges_to_order( $product_data, $order ); } wc_add_order_item_meta( $item_id, '_cartflows_' . $type, 'yes' ); wc_add_order_item_meta( $item_id, '_cartflows_step_id', $product_data['step_id'] ); $order->set_address( $parent_order->get_address( 'billing' ), 'billing' ); $order->set_address( $parent_order->get_address( 'shipping' ), 'shipping' ); // Set shipping data. $order->set_payment_method( $parent_order->get_payment_method() ); $order->set_payment_method_title( $parent_order->get_payment_method_title() ); if ( ! wc_tax_enabled() ) { // Reports won't track orders fix. $order->set_shipping_tax( 0 ); $order->set_cart_tax( 0 ); } $order->calculate_totals(); $offer_orders_meta = $parent_order->get_meta( '_cartflows_offer_child_orders' ); if ( ! is_array( $offer_orders_meta ) ) { $offer_orders_meta = array(); } $offer_orders_meta[ $order->get_id() ] = array( 'type' => $type ); $parent_order->update_meta_data( '_cartflows_offer_child_orders', $offer_orders_meta ); // Cancel the main order if replace order is enabled. $this->cancel_parent_order( $parent_order, $product_data, $type, $order ); // Save the order. $parent_order->save(); // Save the child order. $order->save(); wcf()->logger->log( 'Child Order created - ' . $order->get_id() ); } } if ( $order ) { $transaction_id = $product_data['transaction_id']; remove_action( 'woocommerce_pre_payment_complete', array( Cartflows_Pro_Frontend::get_instance(), 'maybe_setup_upsell' ), 99, 1 ); do_action( 'cartflows_child_offer_before_payment_complete', $order, $product_data, $parent_order ); $this->payment_complete( $order, $transaction_id ); $order->set_transaction_id( $transaction_id ); $order->save(); $transaction_id_note = ''; if ( ! empty( $transaction_id ) ) { $transaction_id_note = sprintf( ' | (Transaction ID: %s)', $transaction_id ); } $order->add_order_note( 'Offer Accepted | ' . $type . ' | Step ID - ' . $product_data['step_id'] . $transaction_id_note ); do_action( 'cartflows_offer_child_order_created', $parent_order, $order, $transaction_id ); do_action( 'cartflows_offer_child_order_created_' . $parent_order->get_payment_method(), $parent_order, $order, $transaction_id ); } wcf()->logger->log( 'End-' . __CLASS__ . '::' . __FUNCTION__ ); return $order; } /** * Cancel the parent order. * * @param object $parent_order order. * @param array $product_data offer data. * @param string $type offer type. * @param object $order child order. */ public function cancel_parent_order( $parent_order, $product_data, $type, $order ) { $is_cancal_main_order = get_post_meta( $product_data['step_id'], 'wcf-replace-main-order', true ); if ( 'yes' === $is_cancal_main_order && $product_data['cancal_main_order'] && ! $parent_order->has_status( 'cancelled' ) ) { do_action( 'cartflows_offer_before_main_order_cancel', $parent_order ); $parent_order->update_status( 'cancelled' ); /* translators: %s step type */ $parent_order->add_order_note( sprintf( __( 'Order has been cancelled as the user has upgraded to the CartFlows %s order.', 'cartflows-pro' ), $type ) ); $parent_order->update_meta_data( '_cartflows_main_order_status', 'cancelled' ); $order->update_meta_data( '_cartflows_offer_amount_diff', $product_data['amount_diff'] ); do_action( 'cartflows_offer_after_main_order_cancel', $parent_order ); } } /** * Complete payment of child order offer. * * @since 1.0.0 * @param object $order order. * @param string $transaction_id Transaction id. */ public function payment_complete( $order, $transaction_id = '' ) { $payment_method = $order->get_payment_method(); if ( 'cod' === $payment_method ) { $order->set_status( 'processing' ); wc_reduce_stock_levels( $order ); } elseif ( 'bacs' === $payment_method ) { $order->set_status( 'on-hold' ); wc_reduce_stock_levels( $order ); } else { $order->payment_complete( $transaction_id ); } } } /** * Kicking this off by calling 'get_instance()' method */ Cartflows_Pro_Orders::get_instance();