1 <?php
  2 /**
  3  * WooCommerce API Orders Class
  4  *
  5  * Handles requests to the /orders endpoint
  6  *
  7  * @author      WooThemes
  8  * @category    API
  9  * @package     WooCommerce/API
 10  * @since       2.1
 11  */
 12 
 13 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
 14 
 15 class WC_API_Orders extends WC_API_Resource {
 16 
 17     /** @var string $base the route base */
 18     protected $base = '/orders';
 19 
 20     /**
 21      * Register the routes for this class
 22      *
 23      * GET /orders
 24      * GET /orders/count
 25      * GET|PUT /orders/<id>
 26      * GET /orders/<id>/notes
 27      *
 28      * @since 2.1
 29      * @param array $routes
 30      * @return array
 31      */
 32     public function register_routes( $routes ) {
 33 
 34         # GET /orders
 35         $routes[ $this->base ] = array(
 36             array( array( $this, 'get_orders' ),     WC_API_Server::READABLE ),
 37         );
 38 
 39         # GET /orders/count
 40         $routes[ $this->base . '/count'] = array(
 41             array( array( $this, 'get_orders_count' ), WC_API_Server::READABLE ),
 42         );
 43 
 44         # GET|PUT /orders/<id>
 45         $routes[ $this->base . '/(?P<id>\d+)' ] = array(
 46             array( array( $this, 'get_order' ),  WC_API_Server::READABLE ),
 47             array( array( $this, 'edit_order' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
 48         );
 49 
 50         # GET /orders/<id>/notes
 51         $routes[ $this->base . '/(?P<id>\d+)/notes' ] = array(
 52             array( array( $this, 'get_order_notes' ), WC_API_Server::READABLE ),
 53         );
 54 
 55         return $routes;
 56     }
 57 
 58     /**
 59      * Get all orders
 60      *
 61      * @since 2.1
 62      * @param string $fields
 63      * @param array $filter
 64      * @param string $status
 65      * @param int $page
 66      * @return array
 67      */
 68     public function get_orders( $fields = null, $filter = array(), $status = null, $page = 1 ) {
 69 
 70         if ( ! empty( $status ) )
 71             $filter['status'] = $status;
 72 
 73         $filter['page'] = $page;
 74 
 75         $query = $this->query_orders( $filter );
 76 
 77         $orders = array();
 78 
 79         foreach( $query->posts as $order_id ) {
 80 
 81             if ( ! $this->is_readable( $order_id ) )
 82                 continue;
 83 
 84             $orders[] = current( $this->get_order( $order_id, $fields ) );
 85         }
 86 
 87         $this->server->add_pagination_headers( $query );
 88 
 89         return array( 'orders' => $orders );
 90     }
 91 
 92 
 93     /**
 94      * Get the order for the given ID
 95      *
 96      * @since 2.1
 97      * @param int $id the order ID
 98      * @param array $fields
 99      * @return array
100      */
101     public function get_order( $id, $fields = null ) {
102 
103         // ensure order ID is valid & user has permission to read
104         $id = $this->validate_request( $id, 'shop_order', 'read' );
105 
106         if ( is_wp_error( $id ) )
107             return $id;
108 
109         $order = new WC_Order( $id );
110 
111         $order_post = get_post( $id );
112 
113         $order_data = array(
114             'id'                        => $order->id,
115             'order_number'              => $order->get_order_number(),
116             'created_at'                => $this->server->format_datetime( $order_post->post_date_gmt ),
117             'updated_at'                => $this->server->format_datetime( $order_post->post_modified_gmt ),
118             'completed_at'              => $this->server->format_datetime( $order->completed_date, true ),
119             'status'                    => $order->status,
120             'currency'                  => $order->order_currency,
121             'total'                     => wc_format_decimal( $order->get_total(), 2 ),
122             'subtotal'                  => wc_format_decimal( $this->get_order_subtotal( $order ), 2 ),
123             'total_line_items_quantity' => $order->get_item_count(),
124             'total_tax'                 => wc_format_decimal( $order->get_total_tax(), 2 ),
125             'total_shipping'            => wc_format_decimal( $order->get_total_shipping(), 2 ),
126             'cart_tax'                  => wc_format_decimal( $order->get_cart_tax(), 2 ),
127             'shipping_tax'              => wc_format_decimal( $order->get_shipping_tax(), 2 ),
128             'total_discount'            => wc_format_decimal( $order->get_total_discount(), 2 ),
129             'cart_discount'             => wc_format_decimal( $order->get_cart_discount(), 2 ),
130             'order_discount'            => wc_format_decimal( $order->get_order_discount(), 2 ),
131             'shipping_methods'          => $order->get_shipping_method(),
132             'payment_details' => array(
133                 'method_id'    => $order->payment_method,
134                 'method_title' => $order->payment_method_title,
135                 'paid'         => isset( $order->paid_date ),
136             ),
137             'billing_address' => array(
138                 'first_name' => $order->billing_first_name,
139                 'last_name'  => $order->billing_last_name,
140                 'company'    => $order->billing_company,
141                 'address_1'  => $order->billing_address_1,
142                 'address_2'  => $order->billing_address_2,
143                 'city'       => $order->billing_city,
144                 'state'      => $order->billing_state,
145                 'postcode'   => $order->billing_postcode,
146                 'country'    => $order->billing_country,
147                 'email'      => $order->billing_email,
148                 'phone'      => $order->billing_phone,
149             ),
150             'shipping_address' => array(
151                 'first_name' => $order->shipping_first_name,
152                 'last_name'  => $order->shipping_last_name,
153                 'company'    => $order->shipping_company,
154                 'address_1'  => $order->shipping_address_1,
155                 'address_2'  => $order->shipping_address_2,
156                 'city'       => $order->shipping_city,
157                 'state'      => $order->shipping_state,
158                 'postcode'   => $order->shipping_postcode,
159                 'country'    => $order->shipping_country,
160             ),
161             'note'                      => $order->customer_note,
162             'customer_ip'               => $order->customer_ip_address,
163             'customer_user_agent'       => $order->customer_user_agent,
164             'customer_id'               => $order->customer_user,
165             'view_order_url'            => $order->get_view_order_url(),
166             'line_items'                => array(),
167             'shipping_lines'            => array(),
168             'tax_lines'                 => array(),
169             'fee_lines'                 => array(),
170             'coupon_lines'              => array(),
171         );
172 
173         // add line items
174         foreach( $order->get_items() as $item_id => $item ) {
175 
176             $product = $order->get_product_from_item( $item );
177 
178             $order_data['line_items'][] = array(
179                 'id'         => $item_id,
180                 'subtotal'   => wc_format_decimal( $order->get_line_subtotal( $item ), 2 ),
181                 'total'      => wc_format_decimal( $order->get_line_total( $item ), 2 ),
182                 'total_tax'  => wc_format_decimal( $order->get_line_tax( $item ), 2 ),
183                 'price'      => wc_format_decimal( $order->get_item_total( $item ), 2 ),
184                 'quantity'   => (int) $item['qty'],
185                 'tax_class'  => ( ! empty( $item['tax_class'] ) ) ? $item['tax_class'] : null,
186                 'name'       => $item['name'],
187                 'product_id' => ( isset( $product->variation_id ) ) ? $product->variation_id : $product->id,
188                 'sku'        => is_object( $product ) ? $product->get_sku() : null,
189             );
190         }
191 
192         // add shipping
193         foreach ( $order->get_shipping_methods() as $shipping_item_id => $shipping_item ) {
194 
195             $order_data['shipping_lines'][] = array(
196                 'id'           => $shipping_item_id,
197                 'method_id'    => $shipping_item['method_id'],
198                 'method_title' => $shipping_item['name'],
199                 'total'        => wc_format_decimal( $shipping_item['cost'], 2 ),
200             );
201         }
202 
203         // add taxes
204         foreach ( $order->get_tax_totals() as $tax_code => $tax ) {
205 
206             $order_data['tax_lines'][] = array(
207                 'code'     => $tax_code,
208                 'title'    => $tax->label,
209                 'total'    => wc_format_decimal( $tax->amount, 2 ),
210                 'compound' => (bool) $tax->is_compound,
211             );
212         }
213 
214         // add fees
215         foreach ( $order->get_fees() as $fee_item_id => $fee_item ) {
216 
217             $order_data['fee_lines'][] = array(
218                 'id'        => $fee_item_id,
219                 'title'     => $fee_item['name'],
220                 'tax_class' => ( ! empty( $fee_item['tax_class'] ) ) ? $fee_item['tax_class'] : null,
221                 'total'     => wc_format_decimal( $order->get_line_total( $fee_item ), 2 ),
222                 'total_tax' => wc_format_decimal( $order->get_line_tax( $fee_item ), 2 ),
223             );
224         }
225 
226         // add coupons
227         foreach ( $order->get_items( 'coupon' ) as $coupon_item_id => $coupon_item ) {
228 
229             $order_data['coupon_lines'][] = array(
230                 'id'     => $coupon_item_id,
231                 'code'   => $coupon_item['name'],
232                 'amount' => wc_format_decimal( $coupon_item['discount_amount'], 2 ),
233             );
234         }
235 
236         return array( 'order' => apply_filters( 'woocommerce_api_order_response', $order_data, $order, $fields, $this->server ) );
237     }
238 
239     /**
240      * Get the total number of orders
241      *
242      * @since 2.1
243      * @param string $status
244      * @param array $filter
245      * @return array
246      */
247     public function get_orders_count( $status = null, $filter = array() ) {
248 
249         if ( ! empty( $status ) )
250             $filter['status'] = $status;
251 
252         $query = $this->query_orders( $filter );
253 
254         if ( ! current_user_can( 'read_private_shop_orders' ) )
255             return new WP_Error( 'woocommerce_api_user_cannot_read_orders_count', __( 'You do not have permission to read the orders count', 'woocommerce' ), array( 'status' => 401 ) );
256 
257         return array( 'count' => (int) $query->found_posts );
258     }
259 
260     /**
261      * Edit an order
262      *
263      * API v1 only allows updating the status of an order
264      *
265      * @since 2.1
266      * @param int $id the order ID
267      * @param array $data
268      * @return array
269      */
270     public function edit_order( $id, $data ) {
271 
272         $id = $this->validate_request( $id, 'shop_order', 'edit' );
273 
274         if ( is_wp_error( $id ) )
275             return $id;
276 
277         $order = new WC_Order( $id );
278 
279         if ( ! empty( $data['status'] ) ) {
280 
281             $order->update_status( $data['status'], isset( $data['note'] ) ? $data['note'] : '' );
282         }
283 
284         return $this->get_order( $id );
285     }
286 
287     /**
288      * Delete an order
289      *
290      * @TODO enable along with POST in 2.2
291      * @param int $id the order ID
292      * @param bool $force true to permanently delete order, false to move to trash
293      * @return array
294      */
295     public function delete_order( $id, $force = false ) {
296 
297         $id = $this->validate_request( $id, 'shop_order', 'delete' );
298 
299         return $this->delete( $id, 'order',  ( 'true' === $force ) );
300     }
301 
302     /**
303      * Get the admin order notes for an order
304      *
305      * @since 2.1
306      * @param int $id the order ID
307      * @param string $fields fields to include in response
308      * @return array
309      */
310     public function get_order_notes( $id, $fields = null ) {
311 
312         // ensure ID is valid order ID
313         $id = $this->validate_request( $id, 'shop_order', 'read' );
314 
315         if ( is_wp_error( $id ) )
316             return $id;
317 
318         $args = array(
319             'post_id' => $id,
320             'approve' => 'approve',
321             'type'    => 'order_note'
322         );
323 
324         remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
325 
326         $notes = get_comments( $args );
327 
328         add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
329 
330         $order_notes = array();
331 
332         foreach ( $notes as $note ) {
333 
334             $order_notes[] = array(
335                 'id'            => $note->comment_ID,
336                 'created_at'    => $this->server->format_datetime( $note->comment_date_gmt ),
337                 'note'          => $note->comment_content,
338                 'customer_note' => get_comment_meta( $note->comment_ID, 'is_customer_note', true ) ? true : false,
339             );
340         }
341 
342         return array( 'order_notes' => apply_filters( 'woocommerce_api_order_notes_response', $order_notes, $id, $fields, $notes, $this->server ) );
343     }
344 
345     /**
346      * Helper method to get order post objects
347      *
348      * @since 2.1
349      * @param array $args request arguments for filtering query
350      * @return WP_Query
351      */
352     private function query_orders( $args ) {
353 
354         // set base query arguments
355         $query_args = array(
356             'fields'      => 'ids',
357             'post_type'   => 'shop_order',
358             'post_status' => 'publish',
359         );
360 
361         // add status argument
362         if ( ! empty( $args['status'] ) ) {
363 
364             $statuses = explode( ',', $args['status'] );
365 
366             $query_args['tax_query'] = array(
367                 array(
368                     'taxonomy' => 'shop_order_status',
369                     'field'    => 'slug',
370                     'terms'    => $statuses,
371                 ),
372             );
373 
374             unset( $args['status'] );
375         }
376 
377         $query_args = $this->merge_query_args( $query_args, $args );
378 
379         return new WP_Query( $query_args );
380     }
381 
382     /**
383      * Helper method to get the order subtotal
384      *
385      * @since 2.1
386      * @param WC_Order $order
387      * @return float
388      */
389     private function get_order_subtotal( $order ) {
390 
391         $subtotal = 0;
392 
393         // subtotal
394         foreach ( $order->get_items() as $item ) {
395 
396             $subtotal += ( isset( $item['line_subtotal'] ) ) ? $item['line_subtotal'] : 0;
397         }
398 
399         return $subtotal;
400     }
401 
402 }
403 
WooCommerce API documentation generated by ApiGen 2.8.0