order before the item name filter, so we run this first if ( class_exists('WC_Composite_Products') ) { $order_item_class = apply_filters( 'woocommerce_order_item_class', '', $item, $this->order ); } // Set item name $data['name'] = apply_filters( 'woocommerce_order_item_name', $item['name'], $item, false ); // Set item quantity $data['quantity'] = $item['qty']; // Set the line total (=after discount) $data['line_total'] = $this->format_price( $item['line_total'] ); $data['single_line_total'] = $this->format_price( $item['line_total'] / max( 1, abs( $item['qty'] ) ) ); $data['line_tax'] = $this->format_price( $item['line_tax'] ); $data['single_line_tax'] = $this->format_price( $item['line_tax'] / max( 1, abs( $item['qty'] ) ) ); $data['tax_rates'] = $this->get_tax_rate( $item, $this->order, false ); $data['calculated_tax_rates'] = $this->get_tax_rate( $item, $this->order, true ); // Set the line subtotal (=before discount) $data['line_subtotal'] = $this->format_price( $item['line_subtotal'] ); $data['line_subtotal_tax'] = $this->format_price( $item['line_subtotal_tax'] ); $data['ex_price'] = $this->get_formatted_item_price( $item, 'total', 'excl' ); $data['price'] = $this->get_formatted_item_price( $item, 'total' ); $data['order_price'] = $this->order->get_formatted_line_subtotal( $item ); // formatted according to WC settings // Calculate the single price with the same rules as the formatted line subtotal (!) // = before discount $data['ex_single_price'] = $this->get_formatted_item_price( $item, 'single', 'excl' ); $data['single_price'] = $this->get_formatted_item_price( $item, 'single' ); // Pass complete item array $data['item'] = $item; // Get the product to add more info if ( is_callable( array( $item, 'get_product' ) ) ) { // WC4.4+ $product = $item->get_product(); } elseif ( defined( 'WOOCOMMERCE_VERSION' ) && version_compare( WOOCOMMERCE_VERSION, '4.4', '<' ) ) { $product = $this->order->get_product_from_item( $item ); } else { $product = null; } // Checking for existence, thanks to MDesigner0 if( !empty( $product ) ) { // Thumbnail (full img tag) $data['thumbnail'] = $this->get_thumbnail( $product ); // Set item SKU $data['sku'] = is_callable( array( $product, 'get_sku' ) ) ? $product->get_sku() : ''; // Set item weight $data['weight'] = is_callable( array( $product, 'get_weight' ) ) ? $product->get_weight() : ''; // Set item dimensions if ( function_exists( 'wc_format_dimensions' ) && is_callable( array( $product, 'get_dimensions' ) ) ) { $data['dimensions'] = wc_format_dimensions( $product->get_dimensions( false ) ); } else { $data['dimensions'] = ''; } // Pass complete product object $data['product'] = $product; } else { $data['product'] = null; } // Set item meta $data['meta'] = wc_display_item_meta( $item, apply_filters( 'wpo_wcpdf_display_item_meta_args', array( 'echo' => false ), $this ) ); $data_list[$item_id] = apply_filters( 'wpo_wcpdf_order_item_data', $data, $this->order, $this->get_type() ); } } return apply_filters( 'wpo_wcpdf_order_items_data', $data_list, $this->order, $this->get_type() ); } /** * Get the tax rates/percentages for an item * @param object $item order item * @param object $order WC_Order * @param bool $force_calculation force calculation of rates rather than retrieving from db * @return string $tax_rates imploded list of tax rates */ public function get_tax_rate( $item, $order, $force_calculation = false ) { $tax_data_container = ( $item['type'] == 'line_item' ) ? 'line_tax_data' : 'taxes'; $tax_data_key = ( $item['type'] == 'line_item' ) ? 'subtotal' : 'total'; $line_total_key = ( $item['type'] == 'line_item' ) ? 'line_total' : 'total'; $line_tax_key = ( $item['type'] == 'shipping' ) ? 'total_tax' : 'line_tax'; $tax_class = isset($item['tax_class']) ? $item['tax_class'] : ''; $line_tax = $item[$line_tax_key]; $line_total = $item[$line_total_key]; $line_tax_data = $item[$tax_data_container]; // first try the easy wc2.2+ way, using line_tax_data if ( !empty( $line_tax_data ) && isset($line_tax_data[$tax_data_key]) ) { $tax_rates = array(); $line_taxes = $line_tax_data[$tax_data_key]; foreach ( $line_taxes as $tax_id => $tax ) { if ( isset($tax) && $tax !== '' ) { $tax_rate = $this->get_tax_rate_by_id( $tax_id, $order ); if ( $tax_rate !== false && $force_calculation === false ) { $tax_rates[] = $tax_rate . ' %'; } else { $tax_rates[] = $this->calculate_tax_rate( $line_total, $line_tax ); } } } // apply decimal setting if (function_exists('wc_get_price_decimal_separator')) { foreach ($tax_rates as &$tax_rate) { $tax_rate = str_replace('.', wc_get_price_decimal_separator(), strval($tax_rate) ); } } $tax_rates = implode(', ', $tax_rates ); return $tax_rates; } if ( $line_tax == 0 ) { return '-'; // no need to determine tax rate... } if ( ! apply_filters( 'wpo_wcpdf_calculate_tax_rate', false ) ) { $tax = new \WC_Tax(); $taxes = $tax->get_rates( $tax_class ); $tax_rates = array(); foreach ($taxes as $tax) { $tax_rates[$tax['label']] = round( $tax['rate'], 2 ).' %'; } if (empty($tax_rates)) { // one last try: manually calculate $tax_rates[] = $this->calculate_tax_rate( $line_total, $line_tax ); } $tax_rates = implode(' ,', $tax_rates ); } return $tax_rates; } public function calculate_tax_rate( $price_ex_tax, $tax ) { $precision = apply_filters( 'wpo_wcpdf_calculate_tax_rate_precision', 1 ); if ( $price_ex_tax != 0) { $tax_rate = round( ($tax / $price_ex_tax)*100, $precision ).' %'; } else { $tax_rate = '-'; } return $tax_rate; } /** * Returns the percentage rate (float) for a given tax rate ID. * @param int $rate_id woocommerce tax rate id * @return float|bool $rate percentage rate */ public function get_tax_rate_by_id( $rate_id, $order = null ) { global $wpdb; // WC 3.7+ stores rate in tax items! if ( $order_rates = $this->get_tax_rates_from_order( $order ) ) { if ( isset( $order_rates[ $rate_id ] ) ) { return (float) $order_rates[ $rate_id ]; } } $rate = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $rate_id ) ); if ($rate === NULL) { return false; } else { return (float) $rate; } } public function get_tax_rates_from_order( $order ) { if ( !empty( $order ) && is_callable( array( $order, 'get_version' ) ) && version_compare( $order->get_version(), '3.7', '>=' ) && version_compare( WC_VERSION, '3.7', '>=' ) ) { $tax_rates = array(); $tax_items = $order->get_items( array('tax') ); if ( empty( $tax_items ) ) { return $tax_rates; } foreach( $tax_items as $tax_item_key => $tax_item ) { if ( is_callable( array( $order, 'get_created_via' ) ) && $order->get_created_via() == 'subscription' ) { // subscription renewals didn't properly record the rate_percent property between WC3.7 and WCS3.0.1 // so we use a fallback if the rate_percent = 0 and the amount != 0 $rate_percent = $tax_item->get_rate_percent(); $tax_amount = $tax_item->get_tax_total() + $tax_item->get_shipping_tax_total(); if ( $tax_amount > 0 && $rate_percent > 0 ) { $tax_rates[ $tax_item->get_rate_id() ] = $rate_percent; } else { continue; // not setting the rate will let the plugin fall back to the rate from the settings } } else { $tax_rates[ $tax_item->get_rate_id() ] = $tax_item->get_rate_percent(); } } return $tax_rates; } else { return false; } } /** * Returns a an array with rate_id => tax rate data (array) of all tax rates in woocommerce * @return array $tax_rate_ids keyed by id */ public function get_tax_rate_ids() { global $wpdb; $rates = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates" ); $tax_rate_ids = array(); foreach ($rates as $rate) { $rate_id = $rate->tax_rate_id; unset($rate->tax_rate_id); $tax_rate_ids[$rate_id] = (array) $rate; } return $tax_rate_ids; } /** * Returns the main product image ID * Adapted from the WC_Product class * (does not support thumbnail sizes) * * @access public * @return string */ public function get_thumbnail_id ( $product ) { $product_id = $product->get_id(); if ( has_post_thumbnail( $product_id ) ) { $thumbnail_id = get_post_thumbnail_id ( $product_id ); } elseif ( ( $parent_id = wp_get_post_parent_id( $product_id ) ) && has_post_thumbnail( $parent_id ) ) { $thumbnail_id = get_post_thumbnail_id ( $parent_id ); } else { $thumbnail_id = false; } return $thumbnail_id; } /** * Returns the thumbnail image tag * * uses the internal WooCommerce/WP functions and extracts the image url or path * rather than the thumbnail ID, to simplify the code and make it possible to * filter for different thumbnail sizes * * @access public * @return string */ public function get_thumbnail ( $product ) { // Get default WooCommerce img tag (url/http) if ( version_compare( WOOCOMMERCE_VERSION, '3.3', '>=' ) ) { $thumbnail_size = 'woocommerce_thumbnail'; } else { $thumbnail_size = 'shop_thumbnail'; } $size = apply_filters( 'wpo_wcpdf_thumbnail_size', $thumbnail_size ); $thumbnail_img_tag_url = $product->get_image( $size, array( 'title' => '' ) ); // Extract the url from img preg_match('/get_thumbnail_id( $product ) ) { $thumbnail_path = get_attached_file( $thumbnail_id ); } } // Thumbnail (full img tag) if ( apply_filters('wpo_wcpdf_use_path', true) && file_exists($thumbnail_path) ) { // load img with server path by default $thumbnail = sprintf('', $thumbnail_path ); } elseif ( apply_filters('wpo_wcpdf_use_path', true) && !file_exists($thumbnail_path) ) { // should use paths but file not found, replace // with http(s):// for dompdf compatibility if ( substr( $thumbnail_url, 0, 2 ) === "//" ) { $prefix = is_ssl() ? 'https://' : 'http://'; $https_thumbnail_url = $prefix . ltrim( $thumbnail_url, '/' ); $thumbnail_img_tag_url = str_replace($thumbnail_url, $https_thumbnail_url, $thumbnail_img_tag_url); } $thumbnail = $thumbnail_img_tag_url; } else { // load img with http url when filtered $thumbnail = $thumbnail_img_tag_url; } /* * PHP GD library can be installed but 'webp' support could be disabled, * which turns the function 'imagecreatefromwebp()' inexistent, * leading to display an error in DOMPDF. * * Check 'System configuration' in the Status tab for 'webp' support. */ if ( 'webp' === wp_check_filetype( $thumbnail_path )['ext'] && ! function_exists( 'imagecreatefromwebp' ) ) { $thumbnail = ''; } // die($thumbnail); return $thumbnail; } /** * Return the order totals listing */ public function get_woocommerce_totals() { // get totals and remove the semicolon $totals = apply_filters( 'wpo_wcpdf_raw_order_totals', $this->order->get_order_item_totals(), $this->order ); // remove the colon for every label foreach ( $totals as $key => $total ) { $label = $total['label']; $colon = strrpos( $label, ':' ); if( $colon !== false ) { $label = substr_replace( $label, '', $colon, 1 ); } $totals[$key]['label'] = $label; } // Fix order_total for refunded orders // not if this is the actual refund! if ( ! $this->is_refund( $this->order ) && apply_filters( 'wpo_wcpdf_remove_refund_totals', true, $this ) ) { $total_refunded = is_callable( array( $this->order, 'get_total_refunded' ) ) ? $this->order->get_total_refunded() : 0; if ( isset($totals['order_total']) && $total_refunded ) { $tax_display = get_option( 'woocommerce_tax_display_cart' ); $totals['order_total']['value'] = wc_price( $this->order->get_total(), array( 'currency' => $this->order->get_currency() ) ); $tax_string = ''; // Tax for inclusive prices if ( wc_tax_enabled() && 'incl' == $tax_display ) { $tax_string_array = array(); if ( 'itemized' == get_option( 'woocommerce_tax_total_display' ) ) { foreach ( $this->order->get_tax_totals() as $code => $tax ) { $tax_amount = $tax->formatted_amount; $tax_string_array[] = sprintf( '%s %s', $tax_amount, $tax->label ); } } else { $tax_string_array[] = sprintf( '%s %s', wc_price( $this->order->get_total_tax(), array( 'currency' => $this->order->get_currency() ) ), WC()->countries->tax_or_vat() ); } if ( ! empty( $tax_string_array ) ) { $tax_string = ' ' . sprintf( __( '(includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) ); } } $totals['order_total']['value'] .= $tax_string; } // remove refund lines (shouldn't be in invoice) foreach ( $totals as $key => $total ) { if ( strpos($key, 'refund_') !== false ) { unset( $totals[$key] ); } } } return apply_filters( 'wpo_wcpdf_woocommerce_totals', $totals, $this->order, $this->get_type() ); } /** * Return/show the order subtotal */ public function get_order_subtotal( $tax = 'excl', $discount = 'incl' ) { // set $tax to 'incl' to include tax, same for $discount //$compound = ($discount == 'incl')?true:false; $subtotal = $this->order->get_subtotal_to_display( false, $tax ); $subtotal = ($pos = strpos($subtotal, ' __('Subtotal', 'woocommerce-pdf-invoices-packing-slips' ), 'value' => $subtotal, ); return apply_filters( 'wpo_wcpdf_order_subtotal', $subtotal, $tax, $discount, $this ); } public function order_subtotal( $tax = 'excl', $discount = 'incl' ) { $subtotal = $this->get_order_subtotal( $tax, $discount ); echo $subtotal['value']; } /** * Return/show the order shipping costs */ public function get_order_shipping( $tax = 'excl' ) { // set $tax to 'incl' to include tax $shipping_cost = $this->order->get_shipping_total(); $shipping_tax = $this->order->get_shipping_tax(); if ($tax == 'excl' ) { $formatted_shipping_cost = $this->format_price( $shipping_cost ); } else { $formatted_shipping_cost = $this->format_price( $shipping_cost + $shipping_tax ); } $shipping = array ( 'label' => __('Shipping', 'woocommerce-pdf-invoices-packing-slips' ), 'value' => $formatted_shipping_cost, 'tax' => $this->format_price( $shipping_tax ), ); return apply_filters( 'wpo_wcpdf_order_shipping', $shipping, $tax, $this ); } public function order_shipping( $tax = 'excl' ) { $shipping = $this->get_order_shipping( $tax ); echo $shipping['value']; } /** * Return/show the total discount */ public function get_order_discount( $type = 'total', $tax = 'incl' ) { if ( $tax == 'incl' ) { switch ($type) { case 'cart': // Cart Discount - pre-tax discounts. (deprecated in WC2.3) $discount_value = $this->order->get_cart_discount(); break; case 'order': // Order Discount - post-tax discounts. (deprecated in WC2.3) $discount_value = $this->order->get_order_discount(); break; case 'total': // Total Discount $discount_value = $this->order->get_total_discount( false ); // $ex_tax = false break; default: // Total Discount - Cart & Order Discounts combined $discount_value = $this->order->get_total_discount(); break; } } else { // calculate discount excluding tax $discount_value = $this->order->get_total_discount( true ); // $ex_tax = true } $discount = array ( 'label' => __('Discount', 'woocommerce-pdf-invoices-packing-slips' ), 'value' => $this->format_price( $discount_value ), 'raw_value' => $discount_value, ); if ( round( $discount_value, 3 ) != 0 ) { return apply_filters( 'wpo_wcpdf_order_discount', $discount, $type, $tax, $this ); } } public function order_discount( $type = 'total', $tax = 'incl' ) { $discount = $this->get_order_discount( $type, $tax ); echo $discount['value']; } /** * Return the order fees */ public function get_order_fees( $tax = 'excl' ) { if ( $_fees = $this->order->get_fees() ) { foreach( $_fees as $id => $fee ) { if ($tax == 'excl' ) { $fee_price = $this->format_price( $fee['line_total'] ); } else { $fee_price = $this->format_price( $fee['line_total'] + $fee['line_tax'] ); } $fees[ $id ] = array( 'label' => $fee['name'], 'value' => $fee_price, 'line_total' => $this->format_price( $fee['line_total'] ), 'line_tax' => $this->format_price( $fee['line_tax'] ) ); } return $fees; } } /** * Return the order taxes */ public function get_order_taxes() { $tax_rate_ids = $this->get_tax_rate_ids(); if ( $order_taxes = $this->order->get_taxes() ) { foreach ( $order_taxes as $key => $tax ) { $taxes[$key] = array( 'label' => $tax->get_label(), 'value' => $this->format_price( $tax->get_tax_total() + $tax->get_shipping_tax_total() ), 'rate_id' => $tax->get_rate_id(), 'tax_amount' => $tax->get_tax_total(), 'shipping_tax_amount' => $tax->get_shipping_tax_total(), 'rate' => isset( $tax_rate_ids[ $tax->get_rate_id() ] ) ? ( (float) $tax_rate_ids[$tax->get_rate_id()]['tax_rate'] ) . ' %': '', ); } return apply_filters( 'wpo_wcpdf_order_taxes', $taxes, $this ); } } /** * Return/show the order grand total */ public function get_order_grand_total( $tax = 'incl' ) { $total_unformatted = $this->order->get_total(); if ($tax == 'excl' ) { $total = $this->format_price( $total_unformatted - $this->order->get_total_tax() ); $label = __( 'Total ex. VAT', 'woocommerce-pdf-invoices-packing-slips' ); } else { $total = $this->format_price( ( $total_unformatted ) ); $label = __( 'Total', 'woocommerce-pdf-invoices-packing-slips' ); } $grand_total = array( 'label' => $label, 'value' => $total, ); return apply_filters( 'wpo_wcpdf_order_grand_total', $grand_total, $tax, $this ); } public function order_grand_total( $tax = 'incl' ) { $grand_total = $this->get_order_grand_total( $tax ); echo $grand_total['value']; } /** * Return/Show shipping notes */ public function get_shipping_notes() { if ( $this->is_refund( $this->order ) ) { $shipping_notes = $this->order->get_reason(); } else { $shipping_notes = wpautop( wptexturize( $this->order->get_customer_note() ) ); } // check document specific setting if ( isset( $this->settings['display_customer_notes'] ) && $this->settings['display_customer_notes'] == 0 ) { $shipping_notes = ''; } if ( apply_filters( 'wpo_wcpdf_shipping_notes_strip_all_tags', false ) ) { $shipping_notes = wp_strip_all_tags( $shipping_notes ); } $shipping_notes = ! empty( $shipping_notes ) ? __( $shipping_notes, 'woocommerce-pdf-invoices-packing-slips' ) : false; return apply_filters( 'wpo_wcpdf_shipping_notes', $shipping_notes, $this ); } public function shipping_notes() { echo $this->get_shipping_notes(); } /** * wrapper for wc_price, ensuring currency is always passed */ public function format_price( $price, $args = array() ) { $args['currency'] = $this->order->get_currency(); $formatted_price = wc_price( $price, $args ); return $formatted_price; } public function wc_price( $price, $args = array() ) { return $this->format_price( $price, $args ); } /** * Gets price - formatted for display. * * @access public * @param mixed $item * @return string */ public function get_formatted_item_price ( $item, $type, $tax_display = '' ) { if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) { return ''; } $divide_by = ($type == 'single' && $item['qty'] != 0 )?abs($item['qty']):1; //divide by 1 if $type is not 'single' (thus 'total') if ( $tax_display == 'excl' ) { $item_price = $this->format_price( ($this->order->get_line_subtotal( $item )) / $divide_by ); } else { $item_price = $this->format_price( ($this->order->get_line_subtotal( $item, true )) / $divide_by ); } return $item_price; } public function get_invoice_number() { // Call the woocommerce_invoice_number filter and let third-party plugins set a number. // Default is null, so we can detect whether a plugin has set the invoice number $third_party_invoice_number = apply_filters( 'woocommerce_invoice_number', null, $this->order_id ); if ($third_party_invoice_number !== null) { return $third_party_invoice_number; } if ( $invoice_number = $this->get_number('invoice') ) { return $formatted_invoice_number = $invoice_number->get_formatted(); } else { return ''; } } public function invoice_number() { echo $this->get_invoice_number(); } public function get_invoice_date() { if ( $invoice_date = $this->get_date('invoice') ) { return $invoice_date->date_i18n( wcpdf_date_format( $this, 'invoice_date' ) ); } else { return ''; } } public function invoice_date() { echo $this->get_invoice_date(); } public function get_document_notes() { if ( $document_notes = $this->get_notes( $this->get_type() ) ) { return $document_notes; } else { return ''; } } public function document_notes() { $document_notes = $this->get_document_notes(); if( $document_notes == strip_tags( $document_notes ) ) { echo nl2br( $document_notes ); } else { echo $document_notes; } } public function document_display_date() { $document_display_date = $this->get_display_date( $this->get_type() ); //If display date data is not available in order meta (for older orders), get the display date information from document settings order meta. if ( empty( $document_display_date ) ) { $document_settings = $this->settings; if( isset( $document_settings['display_date'] ) ) { $document_display_date = $document_settings['display_date']; } else { $document_display_date = 'invoice_date'; } } $formatted_value = $this->get_display_date_label( $document_display_date ); return $formatted_value; } public function get_display_date_label( $date_string ) { $date_labels = array( 'invoice_date' => __( 'Invoice Date' , 'woocommerce-pdf-invoices-packing-slips' ), 'order_date' => __( 'Order Date' , 'woocommerce-pdf-invoices-packing-slips' ), ); if( isset( $date_labels[$date_string] ) ) { return $date_labels[ $date_string ]; } else { return ''; } } } endif; // class_exists