Overview
  • Namespace
  • Class

Namespaces

  • None
  • WC_POS
    • Admin
      • Settings
        • Receipt
    • API
    • Gateways
    • Integrations
    • Products

Classes

  • WC_POS\Activator
  • WC_POS\Admin
  • WC_POS\Admin\Gateways
  • WC_POS\Admin\Menu
  • WC_POS\Admin\Notices
  • WC_POS\Admin\Orders
  • WC_POS\Admin\Page
  • WC_POS\Admin\Permalink
  • WC_POS\Admin\Plugins
  • WC_POS\Admin\Products
  • WC_POS\Admin\Settings
  • WC_POS\Admin\Settings\Access
  • WC_POS\Admin\Settings\Checkout
  • WC_POS\Admin\Settings\Customers
  • WC_POS\Admin\Settings\Gateways
  • WC_POS\Admin\Settings\General
  • WC_POS\Admin\Settings\HotKeys
  • WC_POS\Admin\Settings\Page
  • WC_POS\Admin\Settings\Receipt\Options
  • WC_POS\Admin\Settings\Receipt\Template
  • WC_POS\Admin\Settings\Receipts
  • WC_POS\Admin\Settings\Status
  • WC_POS\Admin\Settings\Tools
  • WC_POS\Admin\Status
  • WC_POS\Ajax
  • WC_POS\API
  • WC_POS\API\Coupons
  • WC_POS\API\Customers
  • WC_POS\API\Gateways
  • WC_POS\API\i18n
  • WC_POS\API\Orders
  • WC_POS\API\Params
  • WC_POS\API\Payload
  • WC_POS\API\Products
  • WC_POS\API\Settings
  • WC_POS\API\Support
  • WC_POS\API\Templates
  • WC_POS\Customers
  • WC_POS\Deactivator
  • WC_POS\Gateways
  • WC_POS\Gateways\Card
  • WC_POS\Gateways\Cash
  • WC_POS\i18n
  • WC_POS\Integrations\Bookings
  • WC_POS\Products
  • WC_POS\Products\Visibility
  • WC_POS\Setup
  • WC_POS\Status
  • WC_POS\Tax
  • WC_POS\Template

Functions

  • is_pos
  • is_pos_admin
  • wc_pos_get_option
  • wc_pos_json_encode
  • wc_pos_locate_template
  • wc_pos_trim_html_string
  • wc_pos_update_option
  • wc_pos_url
  1 <?php
  2 
  3 /**
  4  * POS Orders Class
  5  * duck punches the WC REST API
  6  *
  7  * @class    WC_POS_API_Orders
  8  * @package  WooCommerce POS
  9  * @author   Paul Kilmurray <paul@kilbot.com.au>
 10  * @link     http://www.woopos.com.au
 11  */
 12 
 13 namespace WC_POS\API;
 14 
 15 use WC_API_Server;
 16 use WC_Emails;
 17 use WC_Order;
 18 use WC_POS\Admin\Settings\Checkout;
 19 use WC_POS\Gateways\Cash;
 20 use WP_Error;
 21 
 22 class Orders {
 23 
 24   /** @var string $base the route base */
 25   protected $base = '/orders';
 26 
 27   /** @var array Contains the raw order data */
 28   private $data = array();
 29   private $flag = false;
 30 
 31 
 32   /**
 33    * @param WC_API_Server $server
 34    */
 35   public function __construct( WC_API_Server $server ) {
 36 
 37     // order response
 38     add_filter( 'woocommerce_api_order_response', array( $this, 'order_response' ), 10, 4 );
 39 
 40     // order emails
 41     add_filter( 'woocommerce_email', array( $this, 'woocommerce_email' ), 99 );
 42 
 43     if( substr( $server->path, 0, strlen( $this->base ) ) === $this->base ){
 44       add_filter( 'woocommerce_api_create_order_data', array( $this, 'create_order_data') );
 45       add_filter( 'woocommerce_api_edit_order_data', array( $this, 'edit_order_data'), 10, 2 );
 46       add_action( 'woocommerce_api_create_order', array( $this, 'create_order') );
 47       add_action( 'woocommerce_api_edit_order', array( $this, 'edit_order') );
 48 
 49       // payment
 50       add_action( 'woocommerce_pos_process_payment', array( $this, 'process_payment' ), 10, 2 );
 51       add_action( 'woocommerce_payment_complete', array( $this, 'payment_complete' ), 10, 1 );
 52     }
 53 
 54   }
 55 
 56 
 57   /**
 58    * Register routes for POS Orders
 59    *
 60    * @param array $routes
 61    * @return array
 62    */
 63   public function register_routes( $routes ) {
 64 
 65     # POST /orders/<order_id>/email/<email>
 66     $routes[ $this->base . '/(?P<order_id>\d+)/email/(?P<email>.+)' ] = array(
 67       array( array( $this, 'email_receipt' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA )
 68     );
 69 
 70     return $routes;
 71   }
 72 
 73 
 74   /**
 75    * Create order data
 76    *
 77    * @param array $data
 78    * @return array
 79    */
 80   public function create_order_data(array $data){
 81 
 82     // add filters & actions for create order
 83     add_filter( 'woocommerce_product_tax_class', array( $this, 'product_tax_class' ), 10, 2 );
 84     add_filter( 'woocommerce_get_product_from_item', array( $this, 'get_product_from_item' ), 10, 3 );
 85     add_filter( 'pre_option_woocommerce_tax_based_on', array( $this, 'woocommerce_tax_based_on' ) );
 86     add_filter( 'woocommerce_find_rates', array( $this, 'find_rates'), 10, 2 );
 87     add_action( 'woocommerce_order_add_product', array( $this, 'order_add_product'), 10, 5 );
 88     add_action( 'updated_order_item_meta', array( $this, 'updated_order_item_meta'), 10, 4 );
 89 
 90     // WC API < 2.4 has a bug if $fee['taxable'] = false
 91     // set $fee['taxable'] = true and use random tax_class so not tax is calculated
 92     if( version_compare( WC()->version, '2.4', '<' ) && isset($data['fee_lines']) ){
 93       foreach( $data['fee_lines'] as &$fee ){
 94         if( !isset($fee['taxable']) || $fee['taxable'] == false ){
 95           $fee['taxable'] = true;
 96           $fee['tax_class'] = 'upgrade-woocommerce-' . time();
 97         }
 98       }
 99     }
100 
101     // WC handling of shipping is FUBAR
102     // if order has shipping line, we'll have to calc the tax ourselves
103     // also recalc total tax for negative fee lines
104     $has_fee = isset($data['fee_lines']) && !empty($data['fee_lines']);
105     $has_shipping = isset($data['shipping_lines']) && !empty($data['shipping_lines']);
106 
107     if( $has_shipping )
108       add_action( 'woocommerce_order_add_shipping', array( $this, 'order_add_shipping'), 10, 3 );
109 
110     if( $has_fee || $has_shipping )
111       add_filter( 'update_post_metadata', array( $this, 'update_post_metadata'), 10, 5 );
112 
113     // populate customer data
114     $customer_id = isset( $data['customer_id'] ) ? $data['customer_id'] : 0 ;
115     if( $customer_id ){
116       $data['billing_address'] = $this->get_customer_details($customer_id, 'billing');
117       $data['shipping_address'] = $this->get_customer_details($customer_id, 'shipping');
118     }
119 
120     // store data
121     $this->data = $data;
122 
123     return $this->data;
124   }
125 
126   /**
127    * Edit order data
128    *
129    * @param $data
130    * @param $order_id
131    * @return array
132    */
133   public function edit_order_data(array $data, $order_id){
134     $this->delete_order_items($order_id);
135     return $this->create_order_data($data);
136   }
137 
138   /**
139    * Change the product tax_class
140    * @param $tax_class
141    * @param $product
142    * @return string
143    */
144   public function product_tax_class($tax_class, $product){
145 
146     $id = isset($product->id) ? $product->id: false;
147     $id = isset($product->variation_id) ? $product->variation_id: $id;
148 
149     if($id){
150       $item = $this->get_line_item($id);
151       if( isset( $item['tax_class'] ) ){
152         $tax_class = sanitize_text_field( $item['tax_class'] );
153       }
154     }
155 
156     return $tax_class;
157   }
158 
159   /**
160    * Set taxable
161    * @param $product
162    * @param $item
163    * @param $WC_Product
164    * @return mixed
165    */
166   public function get_product_from_item($product, $item, $WC_Product){
167 
168     $id = isset($product->id) ? $product->id: false;
169     $id = isset($product->variation_id) ? $product->variation_id: $id;
170 
171     if($id){
172       $data = $this->get_line_item($id);
173       if( isset( $data['taxable'] ) ){
174         // api has options true/false
175         // tax_status has options taxable/shipping/none
176         $product->tax_status = $data['taxable'] ? 'taxable' : 'none';
177       }
178     }
179 
180     return $product;
181 
182   }
183 
184   /**
185    * Opportunity to change title and add line_item meta
186    * @param $order_id
187    * @param $item_id
188    * @param $product
189    * @param $qty
190    * @param $args
191    */
192   public function order_add_product($order_id, $item_id, $product, $qty, $args){
193 
194     $id = isset($product->id) ? $product->id: false;
195     $id = isset($product->variation_id) ? $product->variation_id: $id;
196 
197     if($id){
198       $data = $this->get_line_item($id);
199 
200       // update title
201       if( isset( $data['title'] ) ){
202         wc_update_order_item( $item_id,
203           array(
204             'order_item_name' => sanitize_text_field( $data['title'] )
205           )
206         );
207       }
208 
209       // update meta
210       if( isset( $data['meta'] ) && !empty( $data['meta'] ) ){
211         $this->add_product_meta( $item_id, $data['meta'] );
212       }
213 
214     }
215 
216   }
217 
218   /**
219    * Add product meta
220    * @param $item_id
221    * @param array $meta
222    */
223   private function add_product_meta($item_id, array $meta){
224 
225     // line meta
226     foreach($meta as $m) {
227       $label = isset($m['label']) ? $m['label'] : '';
228       $value = isset($m['value']) ? $m['value'] : '';
229       wc_add_order_item_meta( $item_id, $label, $value );
230     }
231 
232   }
233 
234   /**
235    *
236    * @param $order_id
237    * @param $item_id
238    * @param $rate
239    */
240   public function order_add_shipping($order_id, $item_id, $rate){
241     $shipping_line = $this->get_shipping_line($rate, $item_id);
242 
243     if($shipping_line && isset($shipping_line['tax'])){
244       $taxes = array();
245       foreach($shipping_line['tax'] as $k => $tax){
246         if( !empty($tax) ){
247           $taxes[$k] = isset($tax['total']) ? wc_format_decimal( $tax['total'] ) : 0 ;
248         }
249       }
250       wc_update_order_item_meta( $item_id, 'taxes', $taxes );
251     }
252   }
253 
254   /**
255    * Short circuit get_option('woocommerce_tax_based_on') as shop base
256    * @return string
257    */
258   public function woocommerce_tax_based_on(){
259     return 'base';
260   }
261 
262   /**
263    * Remove calc_shipping_tax
264    * @param $matched_tax_rates
265    * @param $args
266    * @return array
267    */
268   public function find_rates( $matched_tax_rates, $args ){
269     if ( $matched_tax_rates ) {
270       foreach ( $matched_tax_rates as &$rate ) {
271         $rate['shipping'] = 'no';
272       }
273     }
274     return $matched_tax_rates;
275   }
276 
277   /**
278    * Retrieve line_item from raw data
279    * @param $product_id
280    */
281   private function get_line_item($product_id){
282     if(!isset($this->data['line_items']))
283       return;
284 
285     foreach($this->data['line_items'] as $item){
286       if( isset($item['product_id']) && $item['product_id'] == $product_id )
287         return $item;
288     }
289   }
290 
291   /**
292    * Match $rate properties to raw data
293    * - there's no id to match so do best guess match id, title & cost
294    * @param $rate
295    * @param $item_id
296    */
297   private function get_shipping_line($rate, $item_id){
298     if(!isset($this->data['shipping_lines']))
299       return;
300 
301     $lines = $this->data['shipping_lines'];
302     foreach($lines as $key => &$line){
303       if(
304         $line['method_id'] == $rate->method_id &&
305         $line['method_title'] == $rate->label &&
306         $line['total'] == $rate->cost &&
307         !isset( $line['id'] )
308       )
309         $line['id'] = $item_id;
310         return $line;
311     }
312   }
313 
314   /**
315    * Fix item meta for negative product/fee values
316    * @param $meta_id
317    * @param $order_id
318    * @param $meta_key
319    * @param $meta_value
320    */
321   public function updated_order_item_meta($meta_id, $order_id, $meta_key, $meta_value){
322 
323     if($meta_key == '_line_tax_data'){
324       $line_subtotal_taxes = isset($meta_value['subtotal']) ? $meta_value['subtotal'] : array();
325       $line_taxes          = isset($meta_value['total']) ? $meta_value['total'] : array();
326       $line_subtotal_tax   = array_sum( $line_subtotal_taxes );
327       $line_tax            = array_sum( $line_taxes );
328 
329       wc_update_order_item_meta( $order_id, '_line_subtotal_tax', wc_format_decimal( $line_subtotal_tax ) );
330       wc_update_order_item_meta( $order_id, '_line_tax', wc_format_decimal( $line_tax ) );
331     }
332 
333   }
334 
335   /**
336    * Apply shipping tax, fix order_tax
337    * -
338    * - filter has already passed $this->data['shipping_tax'] test
339    * @param $null
340    * @param $order_id
341    * @param $meta_key
342    * @param $meta_value
343    * @param $prev_value
344    * @return null
345    */
346   public function update_post_metadata($null, $order_id, $meta_key, $meta_value, $prev_value){
347 
348     // we want last update to _order_shipping after $order->calculate_taxes()
349     // set flag true on first pass
350     if( $meta_key == '_order_shipping_tax' )
351       $this->flag = true;
352 
353     if( $meta_key != '_order_shipping' || ! $this->flag )
354       return $null;
355 
356     // update order meta
357     $shipping_tax_total = isset($this->data['shipping_tax']) ? $this->data['shipping_tax'] : 0 ;
358     update_post_meta( $order_id, '_order_shipping_tax', wc_format_decimal( $shipping_tax_total ) );
359 
360     // check each shipping tax line
361     // if order item meta already exists, update the shipping_tax_amount
362     // if order item meta not present, add the new tax
363     // ... nasty :(
364 
365     // first get an assoc array of $rate_id => $item_id
366     $tax_items = array();
367     $order_tax = 0;
368     $order = wc_get_order( $order_id );
369     foreach ( $order->get_tax_totals() as $code => $tax ) {
370       $tax_items[$tax->rate_id] = $tax->id;
371       $order_tax += $tax->amount;
372     }
373 
374     // fix total_tax calc
375     update_post_meta($order_id, '_order_tax', $order_tax);
376 
377     // now loop through the shipping_lines
378     if( isset( $shipping['shipping_lines'] ) ) :
379       foreach($this->data['shipping_lines'] as $shipping) :
380         if( isset( $shipping['tax'] ) ) :
381           foreach( $shipping['tax'] as $rate_id => $tax ) :
382             if( isset( $tax['total'] ) ) :
383 
384               if( array_key_exists( $rate_id, $tax_items ) ){
385                 wc_update_order_item_meta( $tax_items[$rate_id], 'shipping_tax_amount', wc_format_decimal( $tax['total'] ) );
386               } else {
387                 $order->add_tax( $rate_id, 0, $tax['total'] );
388               }
389 
390             endif;
391           endforeach;
392         endif;
393       endforeach;
394     endif;
395 
396     return $null;
397   }
398 
399   /**
400    * Create order complete
401    * @param $order_id
402    */
403   public function create_order( $order_id ){
404     // pos meta
405     $current_user = wp_get_current_user();
406 
407     update_post_meta( $order_id, '_pos', 1 );
408     update_post_meta( $order_id, '_pos_user', $current_user->ID );
409     update_post_meta( $order_id, '_pos_user_name', $current_user->user_firstname . ' ' . $current_user->user_lastname );
410 
411     // check _order_tax and _order_shipping_tax for reports
412     if( ! get_post_meta( $order_id, '_order_tax', true ) ){
413       update_post_meta( $order_id, '_order_tax', 0 );
414     }
415 
416     if( ! get_post_meta( $order_id, '_order_shipping_tax', true ) ){
417       update_post_meta( $order_id, '_order_shipping_tax', 0 );
418     }
419 
420     // payment
421     do_action( 'woocommerce_pos_process_payment', $order_id, $this->data);
422   }
423 
424   /**
425    * Edit order complete
426    * @param $order_id
427    */
428   public function edit_order( $order_id ){
429     // payment
430     do_action( 'woocommerce_pos_process_payment', $order_id, $this->data);
431   }
432 
433   /**
434    * Process payment
435    * @param $order_id
436    * @param $data
437    */
438   public function process_payment( $order_id, $data ) {
439 
440     if ( !isset( $data[ 'payment_details' ] ) ) {
441       return;
442     }
443 
444     // some gateways check if a user is signed in, so let's switch to customer
445     $logged_in_user = get_current_user_id();
446     $customer_id = isset( $data[ 'customer_id' ] ) ? $data[ 'customer_id' ] : 0;
447     wp_set_current_user( $customer_id );
448 
449     // load the gateways & process payment
450     $gateway_id = $data[ 'payment_details' ][ 'method_id' ];
451     add_filter( 'option_woocommerce_' . $gateway_id . '_settings', array( $this, 'force_enable_gateway' ) );
452     $settings = Checkout::get_instance();
453     $gateways = $settings->load_enabled_gateways();
454 
455     if ( isset( $gateways[ $gateway_id ] ) ) {
456       do_action( 'woocommerce_pos_process_' . strtolower( $gateway_id ) . '_payment', $order_id, $data );
457       $response = $gateways[ $gateway_id ]->process_payment( $order_id );
458       if ( isset( $response[ 'result' ] ) && $response[ 'result' ] == 'success' ) {
459         $this->payment_success( $gateway_id, $order_id, $response );
460       } else {
461         $this->payment_failure( $gateway_id, $order_id, $response );
462       }
463 
464     } else {
465       $this->payment_failure( $gateway_id, $order_id, '' );
466     }
467 
468     // switch back to logged in user
469     wp_set_current_user( $logged_in_user );
470 
471     // clear any payment gateway messages
472     wc_clear_notices();
473   }
474 
475   /**
476    * Some gateways will check if enabled
477    * @param $data
478    * @return mixed
479    */
480   public function force_enable_gateway($data) {
481     if ( isset( $data[ 'enabled' ] ) ) {
482       $data[ 'enabled' ] = 'yes';
483     }
484 
485     return $data;
486   }
487 
488   /**
489    * @param $gateway_id
490    * @param $order_id
491    * @param $response
492    */
493   private function payment_success($gateway_id, $order_id, $response){
494 
495     // capture any instructions
496     ob_start();
497     do_action( 'woocommerce_thankyou_' . $gateway_id, $order_id );
498     $response['messages'] = ob_get_contents();
499     ob_end_clean();
500 
501     // redirect
502     if( isset($response['redirect']) ){
503       $response['messages'] = $this->payment_redirect($gateway_id, $order_id, $response);
504     }
505 
506     update_post_meta( $order_id, '_pos_payment_result', 'success' );
507     update_post_meta( $order_id, '_pos_payment_message', $response['messages'] );
508   }
509 
510   /**
511    * @param $gateway_id
512    * @param $order_id
513    * @param $response
514    */
515   private function payment_failure($gateway_id, $order_id, $response){
516     $message = isset($response['messages']) ? $response['messages'] : wc_get_notices( 'error' );
517 
518     // if messages empty give generic response
519     if(empty($message)){
520       $message = __( 'There was an error processing the payment', 'woocommerce-pos');
521     }
522 
523     update_post_meta( $order_id, '_pos_payment_result', 'failure' );
524     update_post_meta( $order_id, '_pos_payment_message', $message );
525   }
526 
527   /**
528    * @param $gateway_id
529    * @param $order_id
530    * @param $response
531    * @return string
532    */
533   private function payment_redirect($gateway_id, $order_id, $response){
534     $message = $response['messages'];
535 
536     // compare url fragments
537     $success_url = wc_get_endpoint_url( 'order-received', $order_id, get_permalink( wc_get_page_id( 'checkout' ) ) );
538     $success = wp_parse_args( parse_url( $success_url ), array( 'host' => '', 'path' => '' ));
539     $redirect = wp_parse_args( parse_url( $response['redirect'] ), array( 'host' => '', 'path' => '' ));
540 
541     $offsite = $success['host'] !== $redirect['host'];
542     $reload = !$offsite && $success['path'] !== $redirect['path'] && $response['messages'] == '';
543 
544     if($offsite || $reload){
545       update_post_meta( $order_id, '_pos_payment_redirect', $response['redirect'] );
546       $message = __('You are now being redirected offsite to complete the payment. ', 'woocommerce-pos');
547       $message .= sprintf( __('<a href="%s">Click here</a> if you are not redirected automatically. ', 'woocommerce-pos'), $response['redirect'] );
548     }
549 
550     return $message;
551   }
552 
553   /**
554    * @param $order_id
555    */
556   public function payment_complete( $order_id ) {
557 
558     // update order status
559     $order = new WC_Order( $order_id );
560     if( $order->status == 'processing' ) {
561       $message = __('POS Transaction completed.', 'woocommerce-pos');
562       $order->update_status( wc_pos_get_option( 'checkout', 'order_status' ), $message );
563     }
564 
565   }
566 
567   /**
568    * Add any payment messages to API response
569    * Also add subtotal_tax to receipt which is not included for some reason
570    * @param array $order_data
571    * @param $order
572    * @param $fields
573    * @param $server
574    * @return array
575    */
576   public function order_response( $order_data, $order, $fields, $server ) {
577 
578     // add cashier data
579     $order_data['cashier'] = $this->add_cashier_details( $order );
580 
581     // add pos payment info
582     $payment_details = isset( $order_data['payment_details'] ) ? $order_data['payment_details'] : array();
583     $order_data['payment_details'] = $this->add_payment_details( $order, $payment_details );
584 
585     // addresses
586 //    $order_data['billing_address'] = $this->filter_address($order_data['billing_address'], $order);
587 //    $order_data['shipping_address'] = $this->filter_address($order_data['shipping_address'], $order, 'shipping');
588 
589     // allow decimal quantity
590     // fixed in WC 2.4+
591     if( version_compare( WC()->version, '2.4', '<' ) && wc_pos_get_option( 'general', 'decimal_qty' ) ){
592       $order_data['line_items'] = $this->filter_qty($order_data['line_items']);
593     }
594 
595     // add subtotal_tax
596     $subtotal_tax = 0;
597     foreach( $order_data['line_items'] as $item ) {
598       if(isset( $item['subtotal_tax'] )) {
599         $subtotal_tax += wc_format_decimal( $item['subtotal_tax'] );
600       }
601     }
602     $order_data['subtotal_tax'] = wc_format_decimal( $subtotal_tax );
603 
604     // add shipping tax
605     foreach( $order_data['shipping_lines'] as &$item ) {
606       if(isset( $item['id'] )) {
607         $taxes = wc_get_order_item_meta( $item['id'], 'taxes' );
608         $item['total_tax'] = is_array($taxes) ? wc_format_decimal( array_sum($taxes) ) : 0 ;
609       }
610     }
611 
612     // add cart discount tax
613     $order_data['cart_discount_tax'] = get_post_meta($order->id, '_cart_discount_tax', true);
614 
615     return $order_data;
616   }
617 
618   /**
619    * @param $order
620    * @return array
621    */
622   private function add_cashier_details( $order ) {
623 
624     if ( !$cashier_id = get_post_meta( $order->id, '_pos_user', true ) ) {
625       return;
626     }
627 
628     $first_name = get_post_meta( $order->id, '_pos_user_first_name', true );
629     $last_name = get_post_meta( $order->id, '_pos_user_last_name', true );
630     if ( !$first_name && !$last_name && $user_info = get_userdata( $cashier_id ) ) {
631       $first_name = $user_info->first_name;
632       $last_name = $user_info->last_name;
633     }
634 
635     $cashier = array(
636       'id'         => $cashier_id,
637       'first_name' => $first_name,
638       'last_name'  => $last_name
639     );
640 
641     return apply_filters( 'woocommerce_pos_order_response_cashier', $cashier, $order );
642 
643   }
644 
645   /**
646    * @param $order
647    * @param array $payment_details
648    * @return array
649    */
650   private function add_payment_details( $order, array $payment_details ){
651 
652     $payment_details['result']   = get_post_meta( $order->id, '_pos_payment_result', true );
653     $payment_details['message']  = get_post_meta( $order->id, '_pos_payment_message', true );
654     $payment_details['redirect'] = get_post_meta( $order->id, '_pos_payment_redirect', true );
655 
656     if( isset( $payment_details['method_id'] ) && $payment_details['method_id'] == 'pos_cash' ){
657       $payment_details['method_pos_cash'] = Cash::payment_details( $order );
658     }
659 
660     return apply_filters( 'woocommerce_pos_order_response_payment_details', $payment_details, $order );
661 
662   }
663 
664   /**
665    * Adds support for custom address fields
666    * @param $address
667    * @param $order
668    * @param string $type
669    * @return array
670    */
671   private function filter_address( $address, $order, $type = 'billing' ){
672     $fields = apply_filters('woocommerce_admin_'.$type.'_fields', false);
673     if( $fields ){
674       $address = array();
675       foreach($fields as $key => $value){
676         $address[$key] = $order->{$type.'_'.$key};
677       }
678     }
679     return $address;
680   }
681 
682   /**
683    * Get customer details
684    * - mirrors woocommerce/includes/class-wc-ajax.php->get_customer_details()
685    * @param $user_id
686    * @param $type_to_load
687    * @return mixed|void
688    */
689   private function get_customer_details( $user_id, $type_to_load ){
690     $customer_data = array(
691       $type_to_load . '_first_name' => get_user_meta( $user_id, $type_to_load . '_first_name', true ),
692       $type_to_load . '_last_name'  => get_user_meta( $user_id, $type_to_load . '_last_name', true ),
693       $type_to_load . '_company'    => get_user_meta( $user_id, $type_to_load . '_company', true ),
694       $type_to_load . '_address_1'  => get_user_meta( $user_id, $type_to_load . '_address_1', true ),
695       $type_to_load . '_address_2'  => get_user_meta( $user_id, $type_to_load . '_address_2', true ),
696       $type_to_load . '_city'       => get_user_meta( $user_id, $type_to_load . '_city', true ),
697       $type_to_load . '_postcode'   => get_user_meta( $user_id, $type_to_load . '_postcode', true ),
698       $type_to_load . '_country'    => get_user_meta( $user_id, $type_to_load . '_country', true ),
699       $type_to_load . '_state'      => get_user_meta( $user_id, $type_to_load . '_state', true ),
700       $type_to_load . '_email'      => get_user_meta( $user_id, $type_to_load . '_email', true ),
701       $type_to_load . '_phone'      => get_user_meta( $user_id, $type_to_load . '_phone', true ),
702     );
703     $customer_data = apply_filters( 'woocommerce_found_customer_details', $customer_data, $user_id, $type_to_load );
704 
705     // remove billing_ or shipping_ prefix for WC REST API
706     $data = array();
707     foreach( $customer_data as $key => $value ): if($value):
708       $key = str_replace( $type_to_load.'_', '', $key );
709       $data[$key] = $value;
710     endif; endforeach;
711     return $data;
712   }
713 
714   /**
715    * @param $line_items
716    * @return mixed
717    */
718   private function filter_qty($line_items){
719     foreach( $line_items as &$item ){
720       $qty = wc_get_order_item_meta( $item['id'], '_qty' );
721       $item['quantity'] = wc_stock_amount( $qty );
722     }
723     return $line_items;
724   }
725 
726 
727   /**
728    * Delete all order items
729    * @param $order_id
730    */
731   private function delete_order_items($order_id) {
732     global $wpdb;
733     $order_item_ids = $wpdb->get_col( $wpdb->prepare( "
734       SELECT      order_item_id
735       FROM        {$wpdb->prefix}woocommerce_order_items
736       WHERE       order_id = %d
737     ", $order_id ) );
738 
739     foreach ( $order_item_ids as $item_id ) {
740       wc_delete_order_item( $item_id );
741     }
742   }
743 
744   /**
745    * WC email notifications
746    * @param WC_Emails $wc_emails
747    */
748   public function woocommerce_email(WC_Emails $wc_emails) {
749 
750     if( ! wc_pos_get_option( 'checkout', 'customer_emails' ) ){
751       $this->remove_customer_emails($wc_emails);
752     }
753 
754     if( ! wc_pos_get_option( 'checkout', 'admin_emails' ) ){
755       $this->remove_admin_emails($wc_emails);
756     }
757   }
758 
759   /**
760    * @param WC_Emails $wc_emails
761    */
762   private function remove_customer_emails(WC_Emails $wc_emails){
763     remove_action(
764       'woocommerce_order_status_pending_to_processing_notification',
765       array(
766         $wc_emails->emails['WC_Email_Customer_Processing_Order'],
767         'trigger'
768       )
769     );
770     remove_action(
771       'woocommerce_order_status_pending_to_on-hold_notification',
772       array(
773         $wc_emails->emails['WC_Email_Customer_Processing_Order'],
774         'trigger'
775       )
776     );
777     remove_action(
778       'woocommerce_order_status_completed_notification',
779       array(
780         $wc_emails->emails['WC_Email_Customer_Completed_Order'],
781         'trigger'
782       )
783     );
784   }
785 
786   /**
787    * @param WC_Emails $wc_emails
788    */
789   private function remove_admin_emails( WC_Emails $wc_emails){
790     // send 'woocommerce_low_stock_notification'
791     // send 'woocommerce_no_stock_notification'
792     // send 'woocommerce_product_on_backorder_notification'
793     remove_action(
794       'woocommerce_order_status_pending_to_processing_notification',
795       array(
796         $wc_emails->emails['WC_Email_New_Order'],
797         'trigger'
798       )
799     );
800     remove_action(
801       'woocommerce_order_status_pending_to_completed_notification',
802       array(
803         $wc_emails->emails['WC_Email_New_Order'],
804         'trigger'
805       )
806     );
807     remove_action(
808       'woocommerce_order_status_pending_to_on-hold_notification',
809       array(
810         $wc_emails->emails['WC_Email_New_Order'],
811         'trigger'
812       )
813     );
814     remove_action(
815       'woocommerce_order_status_failed_to_processing_notification',
816       array(
817         $wc_emails->emails['WC_Email_New_Order'],
818         'trigger'
819       )
820     );
821     remove_action(
822       'woocommerce_order_status_failed_to_completed_notification',
823       array(
824         $wc_emails->emails['WC_Email_New_Order'],
825         'trigger'
826       )
827     );
828     remove_action(
829       'woocommerce_order_status_failed_to_on-hold_notification',
830       array(
831         $wc_emails->emails['WC_Email_New_Order'],
832         'trigger'
833       )
834     );
835 
836   }
837 
838   /**
839    * Send email receipt
840    *
841    * @param $order_id
842    * @param $email
843    * @return array
844    */
845   static public function email_receipt( $order_id, $email ) {
846     $order = wc_get_order( absint( $order_id ) );
847 
848     if ( is_object( $order ) ) {
849       if ( $email != '' ) {
850         $order->billing_email = $email;
851       }
852       WC()->mailer()->customer_invoice( $order );
853 
854       // hook for third party plugins
855       do_action( 'woocommerce_pos_email_receipt', $email, $order_id, $order );
856 
857       return array(
858         'result'  => 'success',
859         'message' => __( 'Email sent', 'woocommerce-pos' )
860       );
861     }
862 
863     return new WP_Error(
864       'woocommerce_pos_settings_error',
865       __( 'There was an error sending the email', 'woocommerce-pos' ),
866       array( 'status' => 400 )
867     );
868 
869   }
870 
871 }
API documentation generated by ApiGen