XylotrechusZ
<?php
namespace WeDevs\Dokan\Order;
use Exception;
use WC_Order;
use WC_Order_Item_Coupon;
use WC_Order_Refund;
use WeDevs\Dokan\Cache;
use WeDevs\Dokan\Utilities\OrderUtil;
use WeDevs\Dokan\Vendor\Coupon;
use WP_Error;
/**
* Order Management API
*
* @since 2.8
* @since 3.8.0 added HPOS support
*/
class Manager {
/**
* Get all orders
*
* @since 3.0.0
* @since 3.6.3 rewritten to include filters
* @since 3.8.0 added HPOS support
*
* @return WP_Error|int[]|WC_Order[]
*/
public function all( $args = [] ) {
$query_args = apply_filters( 'dokan_get_vendor_orders_args', wp_parse_args( $this->get_backward_compatibility_args( $args ), $args ), $args, [] );
if ( ! OrderUtil::is_hpos_enabled() ) {
$meta = $query_args['meta_query'] ?? [];
unset( $query_args['meta_query'] );
$handle_meta = function ( $query ) use ( $meta ) {
if ( [] === $meta ) {
return $query;
}
if ( ! isset( $query['meta_query'] ) ) {
$query['meta_query'] = $meta; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
} else {
$query['meta_query'] = array_merge( $query['meta_query'], $meta ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
}
return $query;
};
add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', $handle_meta, 10, 1 );
}
$orders = wc_get_orders( $query_args );
if ( ! OrderUtil::is_hpos_enabled() ) {
remove_filter( 'woocommerce_order_data_store_cpt_get_orders_query', $handle_meta, 10 );
}
if ( isset( $args['return'] ) && 'count' === $args['return'] ) {
return apply_filters( 'dokan_get_vendor_orders_count', $orders->total, $args );
}
return apply_filters( 'dokan_get_vendor_orders', $orders, $args );
}
/**
* Get backward compatibility args
*
* @since 3.8.0
*
* @param array $args
*
* @return array
*/
protected function get_backward_compatibility_args( $args = [] ) {
$default = [
'type' => 'shop_order',
'seller_id' => 0,
'customer_id' => 0,
'order_id' => 0,
'status' => 'all',
'order_date' => '', // only for backward compatibility, will be removed in the future, if date is passed in args, it will be ignored
'date' => [
'from' => '',
'to' => '',
],
'search' => '',
'include' => [],
'exclude' => [],
'orderby' => 'date',
'order' => 'DESC',
'limit' => 10,
'paged' => 1, // do nothing if offset is used
'offset' => '',
'return' => 'objects', // objects, ids, count
'meta_query' => [], // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query_relation' => 'AND',
];
$args = wp_parse_args( $args, $default );
$query_args = [
'limit' => $args['limit'],
'paged' => $args['paged'],
'offset' => $args['offset'],
'order' => $args['order'],
'return' => $args['return'],
'meta_query' => $args['meta_query'] ?? [], // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
];
// fix meta query relation
$query_args['meta_query']['relation'] = $args['meta_query_relation'];
// now apply filtering on the field
// filter by seller id
if ( ! $this->is_empty( $args['seller_id'] ) ) {
$seller_ids = array_filter( array_map( 'absint', (array) $args['seller_id'] ) );
$query_args['meta_query'][] = [
[
'key' => '_dokan_vendor_id',
'value' => $seller_ids,
'compare' => 'IN',
'type' => 'NUMERIC',
],
];
}
// filter customer id
if ( ! $this->is_empty( $args['customer_id'] ) ) {
$customer_ids = array_filter( array_map( 'absint', (array) $args['customer_id'] ) );
$query_args['customer_id'] = $customer_ids;
}
// filter order id
if ( ! $this->is_empty( $args['order_id'] ) ) {
$order_ids = array_filter( array_map( 'absint', (array) $args['order_id'] ) );
if ( OrderUtil::is_hpos_enabled() ) {
$query_args['id'] = $order_ids;
} else {
$query_args['post__in'] = $order_ids;
}
}
// filter status
if ( ! $this->is_empty( $args['status'] ) && 'all' !== $args['status'] ) {
$query_args['status'] = (array) $args['status'];
}
// include order ids
if ( ! $this->is_empty( $args['include'] ) ) {
$include = array_filter( array_map( 'absint', (array) $args['include'] ) );
if ( OrderUtil::is_hpos_enabled() ) {
$query_args['id'] = $include;
} else {
$query_args['post__in'] = $include;
}
}
// exclude order ids
if ( ! $this->is_empty( $args['exclude'] ) ) {
$exclude = array_filter( array_map( 'absint', (array) $args['exclude'] ) );
$query_args['exclude'] = $exclude;
}
// date filter
$date_from = false;
$date_to = false;
// check if the start date is set
if ( ! $this->is_empty( $args['date']['from'] ) ) {
// convert date string to object
$date_from = dokan_current_datetime()->modify( $args['date']['from'] );
}
// check if the end date is set
if ( ! $this->is_empty( $args['date']['to'] ) ) {
// convert date string to object
$date_to = dokan_current_datetime()->modify( $args['date']['to'] );
}
if ( $date_from && $date_to ) {
$date_from = $date_from->format( 'Y-m-d' );
$date_to = $date_to->format( 'Y-m-d' );
$query_args['date_created'] = $date_from . '...' . $date_to;
// if only start date is set
} elseif ( $date_from ) {
$query_args['date_created'] = '>=' . $date_from->format( 'Y-m-d' );
// if only end date is set
} elseif ( $date_to ) {
$query_args['date_created'] = '<=' . $date_to->format( 'Y-m-d' );
// if only a single date is set
} elseif ( is_string( $args['date'] ) && ! empty( $args['date'] ) ) {
// backward compatibility for old filter
$query_args['date_created'] = $args['date'];
} elseif ( ! $this->is_empty( $args['order_date'] ) ) {
// backward compatibility for old filter
$query_args['date_created'] = $args['order_date'];
}
// filter by search parameter
if ( ! $this->is_empty( $args['search'] ) ) {
if ( OrderUtil::is_hpos_enabled() ) {
$query_args['id'] = (int) $args['search'];
} else {
$query_args['post__in'] = [ (int) $args['search'] ];
}
}
// fix order by parameter
if ( ! in_array( strtoupper( $args['order'] ), [ 'ASC', 'DESC' ], true ) ) {
$query_args['order'] = 'DESC';
}
// fix order by parameter
if ( isset( $args['order_by'] ) ) {
$args['orderby'] = $args['order_by'];
}
// get supported orderby paramter
$supported_order_by = [
'name' => 'name',
'type' => 'type',
'rand' => 'rand',
'modified' => 'modified',
'post_date' => 'date',
'date' => 'date',
'id' => 'ID',
'order_id' => 'ID',
'seller_id' => 'seller_id',
];
if ( ! array_key_exists( $args['orderby'], $supported_order_by ) ) {
$args['orderby'] = 'date';
}
switch ( $args['orderby'] ) {
case 'seller_id':
$query_args = array_merge(
$query_args, [
'orderby' => 'meta_value',
'meta_key' => '_dokan_vendor_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_type' => 'NUMERIC',
]
);
break;
default:
$query_args['orderby'] = $supported_order_by[ $args['orderby'] ];
break;
}
// fix return parameter
if ( 'count' === $query_args['return'] ) {
$query_args['limit'] = 1;
$query_args['return'] = 'ids';
$query_args['paginate'] = true;
}
return $query_args;
}
/**
* Get single order details
*
* @since 3.0.0
*
* @return bool|WC_Order|WC_Order_Refund
*/
public function get( $id ) {
return wc_get_order( $id );
}
/**
* Count orders for a seller
*
* @since 3.8.0 moved this function from functions.php file
*
* @param int $seller_id
*
* @return array
*/
public function count_orders( $seller_id ) {
global $wpdb;
$cache_group = "seller_order_data_{$seller_id}";
$cache_key = "count_orders_{$seller_id}";
$counts = Cache::get( $cache_key, $cache_group );
if ( false === $counts ) {
$counts = [
'wc-pending' => 0,
'wc-completed' => 0,
'wc-on-hold' => 0,
'wc-processing' => 0,
'wc-refunded' => 0,
'wc-cancelled' => 0,
'wc-failed' => 0,
'wc-checkout-draft' => 0,
'total' => 0,
];
$counts = apply_filters( 'dokan_order_status_count', $counts );
$order_table_name = OrderUtil::get_order_table_name();
if ( OrderUtil::is_hpos_enabled() ) {
// HPOS usage is enabled.
// phpcs:disable
$query = $wpdb->prepare(
"SELECT do.order_status as order_status, count(do.id) as order_count
FROM {$wpdb->prefix}dokan_orders AS do
LEFT JOIN $order_table_name p ON do.order_id = p.id
WHERE
do.seller_id = %d AND
p.status != 'trash'
GROUP BY do.order_status",
[ $seller_id ]
);
// phpcs:enable
} else {
// traditional CPT-based orders are in use.
// phpcs:disable
$query = $wpdb->prepare(
"SELECT do.order_status as order_status, count(do.id) as order_count
FROM {$wpdb->prefix}dokan_orders AS do
LEFT JOIN $order_table_name p ON do.order_id = p.ID
WHERE
do.seller_id = %d AND
p.post_status != 'trash'
GROUP BY do.order_status",
[ $seller_id ]
);
// phpcs:enable
}
$results = $wpdb->get_results( $query ); // phpcs:ignore
if ( $results ) {
foreach ( $results as $count ) {
if ( isset( $counts[ $count->order_status ] ) ) {
$counts[ $count->order_status ] = intval( $count->order_count );
$counts['total'] += $counts[ $count->order_status ];
}
}
}
$counts = (object) $counts;
Cache::set( $cache_key, $counts, $cache_group );
}
return $counts;
}
/**
* Check if an order with same id is exists in database
*
* @since 3.8.0
*
* @param int|WC_Order $order_id
*
* @return boolean
*/
public function is_order_already_synced( $order_id ) {
global $wpdb;
if ( $order_id instanceof WC_Order ) {
$order_id = $order_id->get_id();
}
if ( ! $order_id || ! is_numeric( $order_id ) ) {
return false;
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$order_id = $wpdb->get_var( $wpdb->prepare( "SELECT 1 FROM {$wpdb->prefix}dokan_orders WHERE order_id=%d LIMIT 1", $order_id ) );
return wc_string_to_bool( $order_id );
}
/**
* Check if order is belonged to given seller
*
* @since 3.8.0
*
* @param int $seller_id
* @param int $order_id
*
* @return bool
*/
public function is_seller_has_order( $seller_id, $order_id ) {
global $wpdb;
return 1 === (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"SELECT 1 FROM {$wpdb->prefix}dokan_orders WHERE seller_id = %d AND order_id = %d LIMIT 1",
[ $seller_id, $order_id ]
)
);
}
/**
* Get order of current logged-in users or by given customer id
*
* @since 3.8.0
*
* @param array $args
*
* @return \stdClass|WC_Order[]|int[]
*/
public function get_customer_orders( $args ) {
// fix customer id if not provided
$customer_id = ! empty( $args['customer_id'] ) ? intval( $args['customer_id'] ) : dokan_get_current_user_id();
$args = [
'customer_id' => $customer_id,
'limit' => ! empty( $args['limit'] ) ? intval( $args['limit'] ) : 10,
'paged' => ! empty( $args['paged'] ) ? absint( $args['paged'] ) : 1,
'return' => ! empty( $args['return_type'] ) && in_array( $args['return_type'], [ 'ids', 'objects' ], true ) ? sanitize_text_field( $args['return_type'] ) : 'ids',
];
return $this->all(
apply_filters( 'woocommerce_my_account_my_orders_query', $args )
);
}
/**
* Get Customer Order IDs by Seller
*
* @since 3.8.0
*
* @param int $customer_id
* @param int $seller_id
*
* @return int[]|null on failure
*/
public function get_customer_order_ids_by_seller( $customer_id, $seller_id ) {
if ( ! $customer_id || ! $seller_id ) {
return null;
}
$args = [
'customer_id' => $customer_id,
'seller_id' => $seller_id,
'return' => 'ids',
'limit' => -1,
];
$orders = $this->all( apply_filters( 'dokan_get_customer_orders_by_seller', $args ) );
return ! empty( $orders ) ? $orders : null;
}
/**
* Get all child orders of a parent order
*
* @param int|WC_Order $parent_order
* @param array $args
*
* @return WC_Order[]
*/
public function get_child_orders( $parent_order, array $args = [] ) {
$parent_order_id = is_numeric( $parent_order ) ? $parent_order : $parent_order->get_id();
$default_args = [
'type' => 'shop_order',
'parent' => $parent_order_id,
'limit' => -1,
];
$args = apply_filters(
'dokan_get_child_orders_args',
wp_parse_args( $args, $default_args ),
$parent_order_id
);
return wc_get_orders( $args );
}
/**
* Delete dokan order
*
* @since 3.8.0
*
* @param int $order_id
* @param int|null $seller_id
*
* @return void
*/
public function delete_seller_order( $order_id, $seller_id = null ) {
global $wpdb;
$where = [
'order_id' => $order_id,
];
$where_format = [ '%d' ];
if ( is_numeric( $seller_id ) ) {
$where['seller_id'] = $seller_id;
$where_format[] = '%d';
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$deleted = $wpdb->delete( $wpdb->prefix . 'dokan_orders', $where, $where_format );
if ( false === $deleted ) {
dokan_log( sprintf( '[DeleteSellerOrder] Error while deleting dokan order table data, order_id: %d, Database Error: %s ', $order_id, $wpdb->last_error ) );
return; // since dokan_orders table data couldn't be deleted, returning from here
}
// delete from dokan refund table -> order_id
$deleted = $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"DELETE FROM `{$wpdb->prefix}dokan_refund` WHERE order_id = %d",
[ $order_id ]
)
);
if ( false === $deleted ) {
dokan_log( sprintf( '[DeleteSellerOrder] Error while deleting refund table data, order_id: %d, Database Error: %s ', $order_id, $wpdb->last_error ) );
}
do_action( 'dokan_after_deleting_seller_order', $order_id );
// delete data from vendor balance table -> trn_id, trn_type: dokan_orders, dokan_refund, dokan_withdraw
$deleted = $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"DELETE FROM `{$wpdb->prefix}dokan_vendor_balance`
WHERE trn_id = %d AND trn_type in ( %s, %s, %s )",
[ $order_id, 'dokan_orders', 'dokan_refund', 'dokan_withdraw' ]
)
);
if ( false === $deleted ) {
dokan_log( sprintf( '[DeleteSellerOrder] Error while deleting vendor balance table data, order_id: %d, Database Error: %s ', $order_id, $wpdb->last_error ) );
}
// delete data from reverse withdrawal table -> order_id, trn_type: order_commission, manual_order_commission, order_refund
$deleted = $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"DELETE FROM `{$wpdb->prefix}dokan_reverse_withdrawal`
WHERE trn_id = %d AND trn_type in ( %s, %s, %s )",
[ $order_id, 'order_commission', 'manual_order_commission', 'order_refund' ]
)
);
if ( false === $deleted ) {
dokan_log( sprintf( '[DeleteSellerOrder] Error while deleting dokan reverse withdrawal table data, order_id: %d, Database Error: %s ', $order_id, $wpdb->last_error ) );
}
}
/**
* Delete dokan order with suborders
*
* @since 3.8.0
*
* @param int $order_id
*
* @return void
*/
public function delete_seller_order_with_suborders( $order_id ) {
// delete main order
$this->delete_seller_order( $order_id );
$sub_orders = $this->get_child_orders( $order_id );
if ( $sub_orders ) {
foreach ( $sub_orders as $sub_order ) {
// delete_seller_order for sub_order will be called from Order/Admin/Hooks.php file
$sub_order->delete( true );
}
}
}
/**
* Creates a sub order
*
* @param WC_Order $parent_order
* @param integer $seller_id
* @param array $seller_products
*
* @return void|WP_Error
*/
public function create_sub_order( $parent_order, $seller_id, $seller_products ) {
dokan_log( 'Creating sub order for vendor: #' . $seller_id );
$bill_ship = [
'billing_country',
'billing_first_name',
'billing_last_name',
'billing_company',
'billing_address_1',
'billing_address_2',
'billing_city',
'billing_state',
'billing_postcode',
'billing_email',
'billing_phone',
'shipping_country',
'shipping_first_name',
'shipping_last_name',
'shipping_company',
'shipping_address_1',
'shipping_address_2',
'shipping_city',
'shipping_state',
'shipping_postcode',
];
try {
$order = new WC_Order();
// save other details
$order->set_created_via( 'dokan' );
$order->set_cart_hash( $parent_order->get_cart_hash() );
$order->set_customer_id( $parent_order->get_customer_id() );
$order->set_currency( $parent_order->get_currency() );
$order->set_prices_include_tax( $parent_order->get_prices_include_tax() );
$order->set_customer_ip_address( $parent_order->get_customer_ip_address() );
$order->set_customer_user_agent( $parent_order->get_customer_user_agent() );
$order->set_customer_note( $parent_order->get_customer_note() );
$order->set_payment_method( $parent_order->get_payment_method() );
$order->set_payment_method_title( $parent_order->get_payment_method_title() );
$order->update_meta_data( '_dokan_vendor_id', $seller_id );
// save billing and shipping address
foreach ( $bill_ship as $key ) {
if ( is_callable( [ $order, "set_{$key}" ] ) ) {
$order->{"set_{$key}"}( $parent_order->{"get_{$key}"}() );
}
}
// save other meta data
$order->save(); // need to save order data before passing it to a hook
// now insert line items
$this->create_line_items( $order, $seller_products );
// do shipping
$this->create_shipping( $order, $parent_order );
// do tax
$this->create_taxes( $order, $parent_order, $seller_products );
// add coupons if any
$this->create_coupons( $order, $parent_order );
$order->save(); // need to save order data before passing it to a hook
do_action( 'dokan_create_sub_order_before_calculate_totals', $order, $parent_order, $seller_products );
// finally, let the order re-calculate itself and save
$order->calculate_totals();
$order->set_status( $parent_order->get_status() );
$order->set_parent_id( $parent_order->get_id() );
$order->save();
// update total_sales count for sub-order
wc_update_total_sales_counts( $order->get_id() );
dokan_log( 'Created sub order : #' . $order->get_id() );
do_action( 'dokan_checkout_update_order_meta', $order->get_id(), $seller_id );
} catch ( \Throwable $e ) {
dokan_log( 'Error in create_sub_order: ' . $e->getMessage() );
dokan_log( 'Stack trace: ' . $e->getTraceAsString() );
dokan_log( 'Backtrace at error: ' . wp_debug_backtrace_summary() );
return new WP_Error( 'dokan-suborder-error', $e->getMessage() );
}
}
/**
* Create line items for order
*
* @param object $order wc_get_order
* @param array $products
*
* @return void
*/
private function create_line_items( $order, $products ) {
foreach ( $products as $item ) {
$product_item = new \WC_Order_Item_Product();
$product_item->set_name( $item->get_name() );
$product_item->set_product_id( $item->get_product_id() );
$product_item->set_variation_id( $item->get_variation_id() );
$product_item->set_quantity( $item->get_quantity() );
$product_item->set_tax_class( $item->get_tax_class() );
$product_item->set_subtotal( $item->get_subtotal() );
$product_item->set_subtotal_tax( $item->get_subtotal_tax() );
$product_item->set_total_tax( $item->get_total_tax() );
$product_item->set_total( $item->get_total() );
$product_item->set_taxes( $item->get_taxes() );
$metadata = $item->get_meta_data();
if ( $metadata ) {
foreach ( $metadata as $meta ) {
$product_item->add_meta_data( $meta->key, $meta->value );
}
}
$product_item->add_meta_data( '_dokan_parent_order_item_id', $item->get_id() );
$order->add_item( $product_item );
}
do_action( 'dokan_after_create_line_items', $order );
}
/**
* Create tax line items
*
* @param WC_Order $order
* @param WC_Order $parent_order
* @param array $products
*
* @return void
*/
private function create_taxes( $order, $parent_order, $products ) {
$shipping = $order->get_items( 'shipping' );
$tax_total = 0;
foreach ( $products as $item ) {
$tax_total += $item->get_total_tax();
}
$seller_shipping_total_taxes = array_map(
function ( $item ) {
return $item->get_total_tax();
},
$shipping
);
foreach ( $parent_order->get_taxes() as $tax ) {
$item = new \WC_Order_Item_Tax();
$item->set_props(
[
'rate_id' => $tax->get_rate_id(),
'label' => $tax->get_label(),
'compound' => $tax->get_compound(),
'rate_code' => \WC_Tax::get_rate_code( $tax->get_rate_id() ),
'tax_total' => $tax_total,
'shipping_tax_total' => empty( $seller_shipping_total_taxes ) ? '' : array_sum( $seller_shipping_total_taxes ),
]
);
$order->add_item( $item );
}
}
/**
* Create shipping for a sub-order if neccessary
*
* @param WC_Order $order
* @param WC_Order $parent_order
*
* @return void
*/
private function create_shipping( $order, $parent_order ) {
dokan_log( sprintf( '#%d - Creating Shipping.', $order->get_id() ) );
// Get all shipping methods for parent order
$shipping_methods = $parent_order->get_shipping_methods();
$order_seller_id = (int) dokan_get_seller_id_by_order( $order->get_id() );
$applied_shipping_methods = [];
if ( $shipping_methods ) {
foreach ( $shipping_methods as $method_item_id => $shipping_object ) {
$shipping_seller_id = (int) wc_get_order_item_meta( $method_item_id, 'seller_id', true );
if ( $order_seller_id === $shipping_seller_id ) {
$applied_shipping_methods[] = $shipping_object;
}
}
$applied_shipping_methods = array_filter( $applied_shipping_methods );
}
$applied_shipping_methods[0] = apply_filters_deprecated(
'dokan_shipping_method',
[
$applied_shipping_methods[0] ?? '',
$order->get_id(),
$parent_order,
],
'3.7.19',
'dokan_shipping_methods'
);
$shipping_methods = apply_filters( 'dokan_shipping_methods', $applied_shipping_methods, $order->get_id(), $parent_order );
// bail out if no shipping methods found
if ( empty( $shipping_methods ) ) {
dokan_log( sprintf( '#%d - No shipping method found. Aborting.', $order->get_id() ) );
return;
}
$shipping_totals = 0.0;
foreach ( $shipping_methods as $shipping_method ) {
if ( ! is_a( $shipping_method, 'WC_Order_Item_Shipping' ) ) {
continue;
}
dokan_log( sprintf( '#%d - Adding shipping item.', $order->get_id() ) );
$item = clone $shipping_method;
$item->set_id( 0 );
$shipping_totals += $shipping_method->get_total();
$order->add_item( $item );
}
$order->set_shipping_total( $shipping_totals );
$order->save();
}
/**
* Create coupons for a sub-order if necessary
*
* @param WC_Order $order
* @param WC_Order $parent_order
*
* @return void
*/
private function create_coupons( $order, $parent_order ) {
if ( dokan()->is_pro_exists() && property_exists( dokan_pro(), 'vendor_discount' ) ) {
// remove vendor discount coupon code changes
remove_filter( 'woocommerce_order_get_items', [ dokan_pro()->vendor_discount->woocommerce_hooks, 'replace_coupon_name' ], 10 );
}
$parent_coupons = $parent_order->get_items( 'coupon' );
if ( ! $parent_coupons ) {
return;
}
$seller_id = dokan_get_seller_id_by_order( $order->get_id() );
if ( ! $seller_id ) {
return;
}
$parent_coupons = array_map(
function ( $coupon ) {
/** @var WC_Order_Item_Coupon $coupon */
return $coupon->get_code();
},
$parent_coupons
);
$order_items = $order->get_items();
$used_coupons_data = [];
foreach ( $order_items as $order_item ) {
$item_coupons = $order_item->get_meta( Coupon::DOKAN_COUPON_META_KEY, true );
if ( ! is_array( $item_coupons ) ) {
continue;
}
foreach ( $item_coupons as $code => $item ) {
if ( ! isset( $used_coupons_data[ $code ] ) ) {
$used_coupons_data[ $code ] = 0;
}
if ( in_array( $code, $parent_coupons, true ) ) {
$used_coupons_data[ $code ] += $item['discount'];
}
}
}
foreach ( $used_coupons_data as $code => $total_discount ) {
$coupon = new \WC_Coupon( $code );
$coupon_item = new WC_Order_Item_Coupon();
$coupon_item->set_props(
[
'code' => $code,
'discount' => $total_discount,
'discount_tax' => 0,
]
);
$coupon_info = $coupon->get_short_info();
$coupon_item->add_meta_data( 'coupon_info', $coupon_info );
$coupon_item->add_meta_data( 'coupon_data', $coupon->get_data() );
$order->add_item( $coupon_item );
}
}
/**
* Monitors a new order and attempts to create sub-orders
*
* If an order contains products from multiple vendor, we can't show the order
* to each seller dashboard. That's why we need to divide the main order to
* some sub-orders based on the number of sellers.
*
* @since 3.8.0 added $force_create parameter
*
* @param bool $force_create if this parameter is true, if suborder is already created, they'd be deleted first
*
* @param int $parent_order_id
*
* @return void
*/
public function maybe_split_orders( $parent_order_id, $force_create = false ) {
if ( is_a( $parent_order_id, 'WC_Order' ) ) {
$parent_order = $parent_order_id;
$parent_order_id = $parent_order->get_id();
} else {
$parent_order = $this->get( $parent_order_id );
}
if ( ! $parent_order ) {
//dokan_log( sprintf( 'Invalid Order ID #%d found. Skipping from here.', $parent_order_id ) );
return;
}
// return if order already has been processed by checking if a vendor id has been assigned to it
if ( (int) $parent_order->get_meta( '_dokan_vendor_id' ) > 0 ) {
return;
}
//dokan_log( sprintf( 'New Order #%d created. Init sub order.', $parent_order_id ) );
if ( $force_create || wc_string_to_bool( $parent_order->get_meta( 'has_sub_order' ) ) === true ) {
$child_orders = $this->get_child_orders( $parent_order->get_id() );
foreach ( $child_orders as $child_order ) {
$child_order->delete( true );
}
}
$vendors = dokan_get_sellers_by( $parent_order_id );
if ( empty( $vendors ) ) {
// we didn't find any vendor, so we can't create sub-order
//dokan_log( 'No vendor found, skipping sub order.' );
return;
}
// return if we've only ONE seller
if ( count( $vendors ) === 1 ) {
//dokan_log( '1 vendor only, skipping sub order.' );
$temp = array_keys( $vendors );
$seller_id = reset( $temp );
do_action( 'dokan_create_parent_order', $parent_order, $seller_id );
$parent_order->update_meta_data( '_dokan_vendor_id', $seller_id );
$parent_order->save();
do_action( 'dokan_checkout_update_order_meta', $parent_order_id, $seller_id );
return;
}
// flag it as it has a suborder
$parent_order->update_meta_data( 'has_sub_order', true );
$parent_order->save();
//dokan_log( sprintf( 'Got %s vendors, starting sub order.', count( $vendors ) ) );
// seems like we've got multiple sellers
foreach ( $vendors as $seller_id => $seller_products ) {
$this->create_sub_order( $parent_order, $seller_id, $seller_products );
}
//dokan_log( sprintf( 'Completed sub order for #%d.', $parent_order_id ) );
}
/**
* This will check if given var is empty or not.
*
* @since 3.6.3
*
* @param mixed $item
*
* @return bool
*/
protected function is_empty( $item ) {
if ( empty( $item ) ) {
return true;
}
// if var is an array, check if it's empty or not
if ( is_array( $item ) && isset( $item[0] ) && intval( $item[0] ) === 0 ) {
return true;
}
return false;
}
}