class-wc-order-data-store-cpt.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. <?php
  2. /**
  3. * WC_Order_Data_Store_CPT class file.
  4. *
  5. * @package WooCommerce/Classes
  6. */
  7. if ( ! defined( 'ABSPATH' ) ) {
  8. exit;
  9. }
  10. /**
  11. * WC Order Data Store: Stored in CPT.
  12. *
  13. * @version 3.0.0
  14. */
  15. class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implements WC_Object_Data_Store_Interface, WC_Order_Data_Store_Interface {
  16. /**
  17. * Data stored in meta keys, but not considered "meta" for an order.
  18. *
  19. * @since 3.0.0
  20. * @var array
  21. */
  22. protected $internal_meta_keys = array(
  23. '_customer_user',
  24. '_order_key',
  25. '_order_currency',
  26. '_billing_first_name',
  27. '_billing_last_name',
  28. '_billing_company',
  29. '_billing_address_1',
  30. '_billing_address_2',
  31. '_billing_city',
  32. '_billing_state',
  33. '_billing_postcode',
  34. '_billing_country',
  35. '_billing_email',
  36. '_billing_phone',
  37. '_shipping_first_name',
  38. '_shipping_last_name',
  39. '_shipping_company',
  40. '_shipping_address_1',
  41. '_shipping_address_2',
  42. '_shipping_city',
  43. '_shipping_state',
  44. '_shipping_postcode',
  45. '_shipping_country',
  46. '_completed_date',
  47. '_paid_date',
  48. '_edit_lock',
  49. '_edit_last',
  50. '_cart_discount',
  51. '_cart_discount_tax',
  52. '_order_shipping',
  53. '_order_shipping_tax',
  54. '_order_tax',
  55. '_order_total',
  56. '_payment_method',
  57. '_payment_method_title',
  58. '_transaction_id',
  59. '_customer_ip_address',
  60. '_customer_user_agent',
  61. '_created_via',
  62. '_order_version',
  63. '_prices_include_tax',
  64. '_date_completed',
  65. '_date_paid',
  66. '_payment_tokens',
  67. '_billing_address_index',
  68. '_shipping_address_index',
  69. '_recorded_sales',
  70. '_recorded_coupon_usage_counts',
  71. '_download_permissions_granted',
  72. '_order_stock_reduced',
  73. );
  74. /**
  75. * Method to create a new order in the database.
  76. *
  77. * @param WC_Order $order Order object.
  78. */
  79. public function create( &$order ) {
  80. $order->set_order_key( 'wc_' . apply_filters( 'woocommerce_generate_order_key', uniqid( 'order_' ) ) );
  81. parent::create( $order );
  82. do_action( 'woocommerce_new_order', $order->get_id() );
  83. }
  84. /**
  85. * Read order data. Can be overridden by child classes to load other props.
  86. *
  87. * @param WC_Order $order Order object.
  88. * @param object $post_object Post object.
  89. * @since 3.0.0
  90. */
  91. protected function read_order_data( &$order, $post_object ) {
  92. parent::read_order_data( $order, $post_object );
  93. $id = $order->get_id();
  94. $date_completed = get_post_meta( $id, '_date_completed', true );
  95. $date_paid = get_post_meta( $id, '_date_paid', true );
  96. if ( ! $date_completed ) {
  97. $date_completed = get_post_meta( $id, '_completed_date', true );
  98. }
  99. if ( ! $date_paid ) {
  100. $date_paid = get_post_meta( $id, '_paid_date', true );
  101. }
  102. $order->set_props(
  103. array(
  104. 'order_key' => get_post_meta( $id, '_order_key', true ),
  105. 'customer_id' => get_post_meta( $id, '_customer_user', true ),
  106. 'billing_first_name' => get_post_meta( $id, '_billing_first_name', true ),
  107. 'billing_last_name' => get_post_meta( $id, '_billing_last_name', true ),
  108. 'billing_company' => get_post_meta( $id, '_billing_company', true ),
  109. 'billing_address_1' => get_post_meta( $id, '_billing_address_1', true ),
  110. 'billing_address_2' => get_post_meta( $id, '_billing_address_2', true ),
  111. 'billing_city' => get_post_meta( $id, '_billing_city', true ),
  112. 'billing_state' => get_post_meta( $id, '_billing_state', true ),
  113. 'billing_postcode' => get_post_meta( $id, '_billing_postcode', true ),
  114. 'billing_country' => get_post_meta( $id, '_billing_country', true ),
  115. 'billing_email' => get_post_meta( $id, '_billing_email', true ),
  116. 'billing_phone' => get_post_meta( $id, '_billing_phone', true ),
  117. 'shipping_first_name' => get_post_meta( $id, '_shipping_first_name', true ),
  118. 'shipping_last_name' => get_post_meta( $id, '_shipping_last_name', true ),
  119. 'shipping_company' => get_post_meta( $id, '_shipping_company', true ),
  120. 'shipping_address_1' => get_post_meta( $id, '_shipping_address_1', true ),
  121. 'shipping_address_2' => get_post_meta( $id, '_shipping_address_2', true ),
  122. 'shipping_city' => get_post_meta( $id, '_shipping_city', true ),
  123. 'shipping_state' => get_post_meta( $id, '_shipping_state', true ),
  124. 'shipping_postcode' => get_post_meta( $id, '_shipping_postcode', true ),
  125. 'shipping_country' => get_post_meta( $id, '_shipping_country', true ),
  126. 'payment_method' => get_post_meta( $id, '_payment_method', true ),
  127. 'payment_method_title' => get_post_meta( $id, '_payment_method_title', true ),
  128. 'transaction_id' => get_post_meta( $id, '_transaction_id', true ),
  129. 'customer_ip_address' => get_post_meta( $id, '_customer_ip_address', true ),
  130. 'customer_user_agent' => get_post_meta( $id, '_customer_user_agent', true ),
  131. 'created_via' => get_post_meta( $id, '_created_via', true ),
  132. 'date_completed' => $date_completed,
  133. 'date_paid' => $date_paid,
  134. 'cart_hash' => get_post_meta( $id, '_cart_hash', true ),
  135. 'customer_note' => $post_object->post_excerpt,
  136. )
  137. );
  138. }
  139. /**
  140. * Method to update an order in the database.
  141. *
  142. * @param WC_Order $order Order object.
  143. */
  144. public function update( &$order ) {
  145. // Before updating, ensure date paid is set if missing.
  146. if ( ! $order->get_date_paid( 'edit' ) && version_compare( $order->get_version( 'edit' ), '3.0', '<' ) && $order->has_status( apply_filters( 'woocommerce_payment_complete_order_status', $order->needs_processing() ? 'processing' : 'completed', $order->get_id(), $order ) ) ) {
  147. $order->set_date_paid( $order->get_date_created( 'edit' ) );
  148. }
  149. // Update the order.
  150. parent::update( $order );
  151. do_action( 'woocommerce_update_order', $order->get_id() );
  152. }
  153. /**
  154. * Helper method that updates all the post meta for an order based on it's settings in the WC_Order class.
  155. *
  156. * @param WC_Order $order Order object.
  157. * @since 3.0.0
  158. */
  159. protected function update_post_meta( &$order ) {
  160. $updated_props = array();
  161. $id = $order->get_id();
  162. $meta_key_to_props = array(
  163. '_order_key' => 'order_key',
  164. '_customer_user' => 'customer_id',
  165. '_payment_method' => 'payment_method',
  166. '_payment_method_title' => 'payment_method_title',
  167. '_transaction_id' => 'transaction_id',
  168. '_customer_ip_address' => 'customer_ip_address',
  169. '_customer_user_agent' => 'customer_user_agent',
  170. '_created_via' => 'created_via',
  171. '_date_completed' => 'date_completed',
  172. '_date_paid' => 'date_paid',
  173. '_cart_hash' => 'cart_hash',
  174. );
  175. $props_to_update = $this->get_props_to_update( $order, $meta_key_to_props );
  176. foreach ( $props_to_update as $meta_key => $prop ) {
  177. $value = $order->{"get_$prop"}( 'edit' );
  178. if ( 'date_paid' === $prop ) {
  179. // In 3.0.x we store this as a UTC timestamp.
  180. update_post_meta( $id, $meta_key, ! is_null( $value ) ? $value->getTimestamp() : '' );
  181. // In 2.6.x date_paid was stored as _paid_date in local mysql format.
  182. update_post_meta( $id, '_paid_date', ! is_null( $value ) ? $value->date( 'Y-m-d H:i:s' ) : '' );
  183. } elseif ( 'date_completed' === $prop ) {
  184. // In 3.0.x we store this as a UTC timestamp.
  185. update_post_meta( $id, $meta_key, ! is_null( $value ) ? $value->getTimestamp() : '' );
  186. // In 2.6.x date_paid was stored as _paid_date in local mysql format.
  187. update_post_meta( $id, '_completed_date', ! is_null( $value ) ? $value->date( 'Y-m-d H:i:s' ) : '' );
  188. } else {
  189. update_post_meta( $id, $meta_key, $value );
  190. }
  191. $updated_props[] = $prop;
  192. }
  193. $address_props = array(
  194. 'billing' => array(
  195. '_billing_first_name' => 'billing_first_name',
  196. '_billing_last_name' => 'billing_last_name',
  197. '_billing_company' => 'billing_company',
  198. '_billing_address_1' => 'billing_address_1',
  199. '_billing_address_2' => 'billing_address_2',
  200. '_billing_city' => 'billing_city',
  201. '_billing_state' => 'billing_state',
  202. '_billing_postcode' => 'billing_postcode',
  203. '_billing_country' => 'billing_country',
  204. '_billing_email' => 'billing_email',
  205. '_billing_phone' => 'billing_phone',
  206. ),
  207. 'shipping' => array(
  208. '_shipping_first_name' => 'shipping_first_name',
  209. '_shipping_last_name' => 'shipping_last_name',
  210. '_shipping_company' => 'shipping_company',
  211. '_shipping_address_1' => 'shipping_address_1',
  212. '_shipping_address_2' => 'shipping_address_2',
  213. '_shipping_city' => 'shipping_city',
  214. '_shipping_state' => 'shipping_state',
  215. '_shipping_postcode' => 'shipping_postcode',
  216. '_shipping_country' => 'shipping_country',
  217. ),
  218. );
  219. foreach ( $address_props as $props_key => $props ) {
  220. $props_to_update = $this->get_props_to_update( $order, $props );
  221. foreach ( $props_to_update as $meta_key => $prop ) {
  222. $value = $order->{"get_$prop"}( 'edit' );
  223. update_post_meta( $id, $meta_key, $value );
  224. $updated_props[] = $prop;
  225. $updated_props[] = $props_key;
  226. }
  227. }
  228. parent::update_post_meta( $order );
  229. // If address changed, store concatenated version to make searches faster.
  230. if ( in_array( 'billing', $updated_props, true ) || ! metadata_exists( 'post', $id, '_billing_address_index' ) ) {
  231. update_post_meta( $id, '_billing_address_index', implode( ' ', $order->get_address( 'billing' ) ) );
  232. }
  233. if ( in_array( 'shipping', $updated_props, true ) || ! metadata_exists( 'post', $id, '_shipping_address_index' ) ) {
  234. update_post_meta( $id, '_shipping_address_index', implode( ' ', $order->get_address( 'shipping' ) ) );
  235. }
  236. // If customer changed, update any downloadable permissions.
  237. if ( in_array( 'customer_id', $updated_props, true ) || in_array( 'billing_email', $updated_props, true ) ) {
  238. $data_store = WC_Data_Store::load( 'customer-download' );
  239. $data_store->update_user_by_order_id( $id, $order->get_customer_id(), $order->get_billing_email() );
  240. }
  241. // Mark user account as active.
  242. if ( in_array( 'customer_id', $updated_props, true ) ) {
  243. wc_update_user_last_active( $order->get_customer_id() );
  244. }
  245. do_action( 'woocommerce_order_object_updated_props', $order, $updated_props );
  246. }
  247. /**
  248. * Excerpt for post.
  249. *
  250. * @param WC_Order $order Order object.
  251. * @return string
  252. */
  253. protected function get_post_excerpt( $order ) {
  254. return $order->get_customer_note();
  255. }
  256. /**
  257. * Get amount already refunded.
  258. *
  259. * @param WC_Order $order Order object.
  260. * @return float
  261. */
  262. public function get_total_refunded( $order ) {
  263. global $wpdb;
  264. $total = $wpdb->get_var(
  265. $wpdb->prepare(
  266. "SELECT SUM( postmeta.meta_value )
  267. FROM $wpdb->postmeta AS postmeta
  268. INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
  269. WHERE postmeta.meta_key = '_refund_amount'
  270. AND postmeta.post_id = posts.ID",
  271. $order->get_id()
  272. )
  273. );
  274. return floatval( $total );
  275. }
  276. /**
  277. * Get the total tax refunded.
  278. *
  279. * @param WC_Order $order Order object.
  280. * @return float
  281. */
  282. public function get_total_tax_refunded( $order ) {
  283. global $wpdb;
  284. $total = $wpdb->get_var(
  285. $wpdb->prepare(
  286. "SELECT SUM( order_itemmeta.meta_value )
  287. FROM {$wpdb->prefix}woocommerce_order_itemmeta AS order_itemmeta
  288. INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
  289. INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON ( order_items.order_id = posts.ID AND order_items.order_item_type = 'tax' )
  290. WHERE order_itemmeta.order_item_id = order_items.order_item_id
  291. AND order_itemmeta.meta_key IN ('tax_amount', 'shipping_tax_amount')",
  292. $order->get_id()
  293. )
  294. );
  295. return abs( $total );
  296. }
  297. /**
  298. * Get the total shipping refunded.
  299. *
  300. * @param WC_Order $order Order object.
  301. * @return float
  302. */
  303. public function get_total_shipping_refunded( $order ) {
  304. global $wpdb;
  305. $total = $wpdb->get_var(
  306. $wpdb->prepare(
  307. "SELECT SUM( order_itemmeta.meta_value )
  308. FROM {$wpdb->prefix}woocommerce_order_itemmeta AS order_itemmeta
  309. INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
  310. INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON ( order_items.order_id = posts.ID AND order_items.order_item_type = 'shipping' )
  311. WHERE order_itemmeta.order_item_id = order_items.order_item_id
  312. AND order_itemmeta.meta_key IN ('cost')",
  313. $order->get_id()
  314. )
  315. );
  316. return abs( $total );
  317. }
  318. /**
  319. * Finds an Order ID based on an order key.
  320. *
  321. * @param string $order_key An order key has generated by.
  322. * @return int The ID of an order, or 0 if the order could not be found
  323. */
  324. public function get_order_id_by_order_key( $order_key ) {
  325. global $wpdb;
  326. return $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_order_key' AND meta_value = %s", $order_key ) );
  327. }
  328. /**
  329. * Return count of orders with a specific status.
  330. *
  331. * @param string $status Order status. Function wc_get_order_statuses() returns a list of valid statuses.
  332. * @return int
  333. */
  334. public function get_order_count( $status ) {
  335. global $wpdb;
  336. return absint( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( * ) FROM {$wpdb->posts} WHERE post_type = 'shop_order' AND post_status = %s", $status ) ) );
  337. }
  338. /**
  339. * Get all orders matching the passed in args.
  340. *
  341. * @deprecated 3.1.0 - Use wc_get_orders instead.
  342. * @see wc_get_orders()
  343. *
  344. * @param array $args List of args passed to wc_get_orders().
  345. *
  346. * @return array|object
  347. */
  348. public function get_orders( $args = array() ) {
  349. wc_deprecated_function( 'WC_Order_Data_Store_CPT::get_orders', '3.1.0', 'Use wc_get_orders instead.' );
  350. return wc_get_orders( $args );
  351. }
  352. /**
  353. * Generate meta query for wc_get_orders.
  354. *
  355. * @param array $values List of customers ids or emails.
  356. * @param string $relation 'or' or 'and' relation used to build the WP meta_query.
  357. * @return array
  358. */
  359. private function get_orders_generate_customer_meta_query( $values, $relation = 'or' ) {
  360. $meta_query = array(
  361. 'relation' => strtoupper( $relation ),
  362. 'customer_emails' => array(
  363. 'key' => '_billing_email',
  364. 'value' => array(),
  365. 'compare' => 'IN',
  366. ),
  367. 'customer_ids' => array(
  368. 'key' => '_customer_user',
  369. 'value' => array(),
  370. 'compare' => 'IN',
  371. ),
  372. );
  373. foreach ( $values as $value ) {
  374. if ( is_array( $value ) ) {
  375. $query_part = $this->get_orders_generate_customer_meta_query( $value, 'and' );
  376. if ( is_wp_error( $query_part ) ) {
  377. return $query_part;
  378. }
  379. $meta_query[] = $query_part;
  380. } elseif ( is_email( $value ) ) {
  381. $meta_query['customer_emails']['value'][] = sanitize_email( $value );
  382. } elseif ( is_numeric( $value ) ) {
  383. $meta_query['customer_ids']['value'][] = strval( absint( $value ) );
  384. } else {
  385. return new WP_Error( 'woocommerce_query_invalid', __( 'Invalid customer query.', 'woocommerce' ), $values );
  386. }
  387. }
  388. if ( empty( $meta_query['customer_emails']['value'] ) ) {
  389. unset( $meta_query['customer_emails'] );
  390. unset( $meta_query['relation'] );
  391. }
  392. if ( empty( $meta_query['customer_ids']['value'] ) ) {
  393. unset( $meta_query['customer_ids'] );
  394. unset( $meta_query['relation'] );
  395. }
  396. return $meta_query;
  397. }
  398. /**
  399. * Get unpaid orders after a certain date,
  400. *
  401. * @param int $date Timestamp.
  402. * @return array
  403. */
  404. public function get_unpaid_orders( $date ) {
  405. global $wpdb;
  406. $unpaid_orders = $wpdb->get_col(
  407. $wpdb->prepare(
  408. // @codingStandardsIgnoreStart
  409. "SELECT posts.ID
  410. FROM {$wpdb->posts} AS posts
  411. WHERE posts.post_type IN ('" . implode( "','", wc_get_order_types() ) . "')
  412. AND posts.post_status = 'wc-pending'
  413. AND posts.post_modified < %s",
  414. // @codingStandardsIgnoreEnd
  415. date( 'Y-m-d H:i:s', absint( $date ) )
  416. )
  417. );
  418. return $unpaid_orders;
  419. }
  420. /**
  421. * Search order data for a term and return ids.
  422. *
  423. * @param string $term Searched term.
  424. * @return array of ids
  425. */
  426. public function search_orders( $term ) {
  427. global $wpdb;
  428. /**
  429. * Searches on meta data can be slow - this lets you choose what fields to search.
  430. * 3.0.0 added _billing_address and _shipping_address meta which contains all address data to make this faster.
  431. * This however won't work on older orders unless updated, so search a few others (expand this using the filter if needed).
  432. *
  433. * @var array
  434. */
  435. $search_fields = array_map(
  436. 'wc_clean', apply_filters(
  437. 'woocommerce_shop_order_search_fields', array(
  438. '_billing_address_index',
  439. '_shipping_address_index',
  440. '_billing_last_name',
  441. '_billing_email',
  442. )
  443. )
  444. );
  445. $order_ids = array();
  446. if ( is_numeric( $term ) ) {
  447. $order_ids[] = absint( $term );
  448. }
  449. if ( ! empty( $search_fields ) ) {
  450. $order_ids = array_unique(
  451. array_merge(
  452. $order_ids,
  453. $wpdb->get_col(
  454. $wpdb->prepare(
  455. "SELECT DISTINCT p1.post_id FROM {$wpdb->postmeta} p1 WHERE p1.meta_value LIKE %s AND p1.meta_key IN ('" . implode( "','", array_map( 'esc_sql', $search_fields ) ) . "')", // @codingStandardsIgnoreLine
  456. '%' . $wpdb->esc_like( wc_clean( $term ) ) . '%'
  457. )
  458. ),
  459. $wpdb->get_col(
  460. $wpdb->prepare(
  461. "SELECT order_id
  462. FROM {$wpdb->prefix}woocommerce_order_items as order_items
  463. WHERE order_item_name LIKE %s",
  464. '%' . $wpdb->esc_like( wc_clean( $term ) ) . '%'
  465. )
  466. )
  467. )
  468. );
  469. }
  470. return apply_filters( 'woocommerce_shop_order_search_results', $order_ids, $term, $search_fields );
  471. }
  472. /**
  473. * Gets information about whether permissions were generated yet.
  474. *
  475. * @param WC_Order|int $order Order ID or order object.
  476. * @return bool
  477. */
  478. public function get_download_permissions_granted( $order ) {
  479. $order_id = WC_Order_Factory::get_order_id( $order );
  480. return wc_string_to_bool( get_post_meta( $order_id, '_download_permissions_granted', true ) );
  481. }
  482. /**
  483. * Stores information about whether permissions were generated yet.
  484. *
  485. * @param WC_Order|int $order Order ID or order object.
  486. * @param bool $set True or false.
  487. */
  488. public function set_download_permissions_granted( $order, $set ) {
  489. $order_id = WC_Order_Factory::get_order_id( $order );
  490. update_post_meta( $order_id, '_download_permissions_granted', wc_bool_to_string( $set ) );
  491. }
  492. /**
  493. * Gets information about whether sales were recorded.
  494. *
  495. * @param WC_Order|int $order Order ID or order object.
  496. * @return bool
  497. */
  498. public function get_recorded_sales( $order ) {
  499. $order_id = WC_Order_Factory::get_order_id( $order );
  500. return wc_string_to_bool( get_post_meta( $order_id, '_recorded_sales', true ) );
  501. }
  502. /**
  503. * Stores information about whether sales were recorded.
  504. *
  505. * @param WC_Order|int $order Order ID or order object.
  506. * @param bool $set True or false.
  507. */
  508. public function set_recorded_sales( $order, $set ) {
  509. $order_id = WC_Order_Factory::get_order_id( $order );
  510. update_post_meta( $order_id, '_recorded_sales', wc_bool_to_string( $set ) );
  511. }
  512. /**
  513. * Gets information about whether coupon counts were updated.
  514. *
  515. * @param WC_Order|int $order Order ID or order object.
  516. * @return bool
  517. */
  518. public function get_recorded_coupon_usage_counts( $order ) {
  519. $order_id = WC_Order_Factory::get_order_id( $order );
  520. return wc_string_to_bool( get_post_meta( $order_id, '_recorded_coupon_usage_counts', true ) );
  521. }
  522. /**
  523. * Stores information about whether coupon counts were updated.
  524. *
  525. * @param WC_Order|int $order Order ID or order object.
  526. * @param bool $set True or false.
  527. */
  528. public function set_recorded_coupon_usage_counts( $order, $set ) {
  529. $order_id = WC_Order_Factory::get_order_id( $order );
  530. update_post_meta( $order_id, '_recorded_coupon_usage_counts', wc_bool_to_string( $set ) );
  531. }
  532. /**
  533. * Gets information about whether stock was reduced.
  534. *
  535. * @param WC_Order|int $order Order ID or order object.
  536. * @return bool
  537. */
  538. public function get_stock_reduced( $order ) {
  539. $order_id = WC_Order_Factory::get_order_id( $order );
  540. return wc_string_to_bool( get_post_meta( $order_id, '_order_stock_reduced', true ) );
  541. }
  542. /**
  543. * Stores information about whether stock was reduced.
  544. *
  545. * @param WC_Order|int $order Order ID or order object.
  546. * @param bool $set True or false.
  547. */
  548. public function set_stock_reduced( $order, $set ) {
  549. $order_id = WC_Order_Factory::get_order_id( $order );
  550. update_post_meta( $order_id, '_order_stock_reduced', wc_bool_to_string( $set ) );
  551. }
  552. /**
  553. * Get the order type based on Order ID.
  554. *
  555. * @since 3.0.0
  556. * @param int $order_id Order ID.
  557. * @return string
  558. */
  559. public function get_order_type( $order_id ) {
  560. return get_post_type( $order_id );
  561. }
  562. /**
  563. * Get valid WP_Query args from a WC_Order_Query's query variables.
  564. *
  565. * @since 3.1.0
  566. * @param array $query_vars query vars from a WC_Order_Query.
  567. * @return array
  568. */
  569. protected function get_wp_query_args( $query_vars ) {
  570. // Map query vars to ones that get_wp_query_args or WP_Query recognize.
  571. $key_mapping = array(
  572. 'customer_id' => 'customer_user',
  573. 'status' => 'post_status',
  574. 'currency' => 'order_currency',
  575. 'version' => 'order_version',
  576. 'discount_total' => 'cart_discount',
  577. 'discount_tax' => 'cart_discount_tax',
  578. 'shipping_total' => 'order_shipping',
  579. 'shipping_tax' => 'order_shipping_tax',
  580. 'cart_tax' => 'order_tax',
  581. 'total' => 'order_total',
  582. 'page' => 'paged',
  583. );
  584. foreach ( $key_mapping as $query_key => $db_key ) {
  585. if ( isset( $query_vars[ $query_key ] ) ) {
  586. $query_vars[ $db_key ] = $query_vars[ $query_key ];
  587. unset( $query_vars[ $query_key ] );
  588. }
  589. }
  590. // Add the 'wc-' prefix to status if needed.
  591. if ( ! empty( $query_vars['post_status'] ) ) {
  592. if ( is_array( $query_vars['post_status'] ) ) {
  593. foreach ( $query_vars['post_status'] as &$status ) {
  594. $status = wc_is_order_status( 'wc-' . $status ) ? 'wc-' . $status : $status;
  595. }
  596. } else {
  597. $query_vars['post_status'] = wc_is_order_status( 'wc-' . $query_vars['post_status'] ) ? 'wc-' . $query_vars['post_status'] : $query_vars['post_status'];
  598. }
  599. }
  600. $wp_query_args = parent::get_wp_query_args( $query_vars );
  601. if ( ! isset( $wp_query_args['date_query'] ) ) {
  602. $wp_query_args['date_query'] = array();
  603. }
  604. if ( ! isset( $wp_query_args['meta_query'] ) ) {
  605. $wp_query_args['meta_query'] = array();
  606. }
  607. $date_queries = array(
  608. 'date_created' => 'post_date',
  609. 'date_modified' => 'post_modified',
  610. 'date_completed' => '_date_completed',
  611. 'date_paid' => '_date_paid',
  612. );
  613. foreach ( $date_queries as $query_var_key => $db_key ) {
  614. if ( isset( $query_vars[ $query_var_key ] ) && '' !== $query_vars[ $query_var_key ] ) {
  615. // Remove any existing meta queries for the same keys to prevent conflicts.
  616. $existing_queries = wp_list_pluck( $wp_query_args['meta_query'], 'key', true );
  617. $meta_query_index = array_search( $db_key, $existing_queries, true );
  618. if ( false !== $meta_query_index ) {
  619. unset( $wp_query_args['meta_query'][ $meta_query_index ] );
  620. }
  621. $wp_query_args = $this->parse_date_for_wp_query( $query_vars[ $query_var_key ], $db_key, $wp_query_args );
  622. }
  623. }
  624. if ( isset( $query_vars['customer'] ) && '' !== $query_vars['customer'] && array() !== $query_vars['customer'] ) {
  625. $values = is_array( $query_vars['customer'] ) ? $query_vars['customer'] : array( $query_vars['customer'] );
  626. $customer_query = $this->get_orders_generate_customer_meta_query( $values );
  627. if ( is_wp_error( $customer_query ) ) {
  628. $wp_query_args['errors'][] = $customer_query;
  629. } else {
  630. $wp_query_args['meta_query'][] = $customer_query;
  631. }
  632. }
  633. if ( isset( $query_vars['anonymized'] ) ) {
  634. if ( $query_vars['anonymized'] ) {
  635. $wp_query_args['meta_query'][] = array(
  636. 'key' => '_anonymized',
  637. 'value' => 'yes',
  638. );
  639. } else {
  640. $wp_query_args['meta_query'][] = array(
  641. 'key' => '_anonymized',
  642. 'compare' => 'NOT EXISTS',
  643. );
  644. }
  645. }
  646. if ( ! isset( $query_vars['paginate'] ) || ! $query_vars['paginate'] ) {
  647. $wp_query_args['no_found_rows'] = true;
  648. }
  649. return apply_filters( 'woocommerce_order_data_store_cpt_get_orders_query', $wp_query_args, $query_vars, $this );
  650. }
  651. /**
  652. * Query for Orders matching specific criteria.
  653. *
  654. * @since 3.1.0
  655. *
  656. * @param array $query_vars query vars from a WC_Order_Query.
  657. *
  658. * @return array|object
  659. */
  660. public function query( $query_vars ) {
  661. $args = $this->get_wp_query_args( $query_vars );
  662. if ( ! empty( $args['errors'] ) ) {
  663. $query = (object) array(
  664. 'posts' => array(),
  665. 'found_posts' => 0,
  666. 'max_num_pages' => 0,
  667. );
  668. } else {
  669. $query = new WP_Query( $args );
  670. }
  671. $orders = ( isset( $query_vars['return'] ) && 'ids' === $query_vars['return'] ) ? $query->posts : array_filter( array_map( 'wc_get_order', $query->posts ) );
  672. if ( isset( $query_vars['paginate'] ) && $query_vars['paginate'] ) {
  673. return (object) array(
  674. 'orders' => $orders,
  675. 'total' => $query->found_posts,
  676. 'max_num_pages' => $query->max_num_pages,
  677. );
  678. }
  679. return $orders;
  680. }
  681. /**
  682. * Return the order type of a given item which belongs to WC_Order.
  683. *
  684. * @since 3.2.0
  685. * @param WC_Order $order Order Object.
  686. * @param int $order_item_id Order item id.
  687. * @return string Order Item type
  688. */
  689. public function get_order_item_type( $order, $order_item_id ) {
  690. global $wpdb;
  691. return $wpdb->get_var( $wpdb->prepare( "SELECT DISTINCT order_item_type FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d and order_item_id = %d;", $order->get_id(), $order_item_id ) );
  692. }
  693. }