1

Inspired by Additional columns and item values in Woocommerce email order details answer That overrides woocommerce templates to add additional columns to email order details.

I want to achieve something similar with different separated columns like:

  1. Quantity
  2. Image and product name
  3. SKU
  4. Product Price $45 Excl.Vat $37.5
  5. Line total $90 Excl. Vat $75

Here is my attempted code:

• Template file email-order-details.ph:

<?php
/**
 * Order details table shown in emails.
 *
 * This template can be overridden by copying it to yourtheme/woocommerce/emails/email-order-details.php.
 *
 * HOWEVER, on occasion WooCommerce will need to update template files and you
 * (the theme developer) will need to copy the new files to your theme to
 * maintain compatibility. We try to do this as little as possible, but it does
 * happen. When this occurs the version of the template file will be bumped and
 * the readme will list any important changes.
 *
 * @see https://docs.woocommerce.com/document/template-structure/
 * @package WooCommerce/Templates/Emails
 * @version 3.3.1
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

$text_align = is_rtl() ? 'right' : 'left';

do_action( 'woocommerce_email_before_order_table', $order, $sent_to_admin, $plain_text, $email ); ?>

<h2>
    <?php
    if ( $sent_to_admin ) {
        $before = '<a class="link" href="' . esc_url( $order->get_edit_order_url() ) . '">';
        $after  = '</a>';
    } else {
        $before = '';
        $after  = '';
    }
    /* translators: %s: Order ID. */
    echo wp_kses_post( $before . sprintf( __( 'Order #%s', 'woocommerce' ) . $after . ' (<time datetime="%s">%s</time>)', $order->get_order_number(), $order->get_date_created()->format( 'c' ), wc_format_datetime( $order->get_date_created() ) ) );
    ?>
</h2>

<div style="margin-bottom: 40px;">
    <table class="td" cellspacing="0" cellpadding="6" style="width: 100%; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;" border="1">
        <thead>
            <tr>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Product', 'woocommerce' ); ?></th>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e(  'Quantity', 'woocommerce' )?></th>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Product SKU', 'woocommerce' ); ?></th>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Price', 'woocommerce' ); ?></th>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Total', 'woocommerce' ); ?></th>
            </tr>
        </thead>
        <tbody>
            <?php
            echo wc_get_email_order_items( $order, array( // WPCS: XSS ok.
                'show_sku'      => $sent_to_admin,
                'show_image'    => true,
                'image_size'    => array( 32, 32 ),
                'plain_text'    => $plain_text,
                'sent_to_admin' => $sent_to_admin,
            ) );
            ?>
        </tbody>
        <tfoot>
            <?php
            $totals = $order->get_order_item_totals();

            if ( $totals ) {
                $i = 0;
                foreach ( $totals as $total ) {
                    $i++;
                    ?>
                    <tr>
                        <th class="td" scope="row" colspan="4" style="text-align:<?php echo esc_attr( $text_align ); ?>; <?php echo ( 1 === $i ) ? 'border-top-width: 4px;' : ''; ?>"><?php echo wp_kses_post( $total['label'] ); ?></th>
                        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; <?php echo ( 1 === $i ) ? 'border-top-width: 4px;' : ''; ?>"><?php echo wp_kses_post( $total['value'] ); ?></td>
                    </tr>
                    <?php
                }
            }
            if ( $order->get_customer_note() ) {
                ?>
                <tr>
                    <th class="td" scope="row" colspan="4" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Additional notes:', 'woocommerce' ); ?></th>
                    <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php echo wp_kses_post( wptexturize( $order->get_customer_note() ) ); ?></td>
                </tr>
                <?php
            }
            ?>
        </tfoot>
    </table>
</div>

<?php do_action( 'woocommerce_email_after_order_table', $order, $sent_to_admin, $plain_text, $email ); ?>

• Template file email-order-items.php:

<?php
/**
 * Email Order Items
 *
 * This template can be overridden by copying it to yourtheme/woocommerce/emails/email-order-items.php.
 *
 * HOWEVER, on occasion WooCommerce will need to update template files and you
 * (the theme developer) will need to copy the new files to your theme to
 * maintain compatibility. We try to do this as little as possible, but it does
 * happen. When this occurs the version of the template file will be bumped and
 * the readme will list any important changes.
 *
 * @see     https://docs.woocommerce.com/document/template-structure/
 * @package WooCommerce/Templates/Emails
 * @version 3.5.0
 */

defined( 'ABSPATH' ) || exit;

$text_align = is_rtl() ? 'right' : 'left';

foreach ( $items as $item_id => $item ) :
    $product       = $item->get_product();
    $sku           = '';
    $purchase_note = '';
    $image         = '';

    if ( ! apply_filters( 'woocommerce_order_item_visible', true, $item ) ) {
        continue;
    }

    if ( is_object( $product ) ) {
        $sku           = $product->get_sku();
        $purchase_note = $product->get_purchase_note();
        $image         = $product->get_image( $image_size );
    }

    ?>
    <tr class="<?php echo esc_attr( apply_filters( 'woocommerce_order_item_class', 'order_item', $item, $order ) ); ?>">
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
            <?php echo wp_kses_post( apply_filters( 'woocommerce_order_item_name', $item->get_name(), $item, false ) );      // SKU.
        if ( $show_sku && $sku ) {
            echo wp_kses_post( ' (#' . $sku . ')' );
        }

        // allow other plugins to add additional product information here.
        do_action( 'woocommerce_order_item_meta_start', $item_id, $item, $order, $plain_text );

        wc_display_item_meta( $item, array(
            'label_before' => '<strong class="wc-item-meta-label" style="float: left; margin-right: .25em; clear: both">',
        ) );

        // allow other plugins to add additional product information here.
        do_action( 'woocommerce_order_item_meta_end', $item_id, $item, $order, $plain_text );
 ?>
        </td>
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align: middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; word-wrap:break-word;">
        <?php

        // Product Quantity.
        echo wp_kses_post( apply_filters( 'woocommerce_email_order_item_quantity', $item->get_quantity(), $item ) );

        ?>
        </td>
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
            <?php echo wp_kses_post( $sku ); ?>
        </td>
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
            <?php echo wp_kses_post( wc_price( round( $item->get_subtotal(), wc_get_price_decimals() ) ) ); ?>
        </td>
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
            <?php echo wp_kses_post( wc_price( round( $item->get_subtotal_tax(), wc_get_price_decimals() ) ) ); ?>
        </td>
    </tr>
    <?php

    if ( $show_purchase_note && $purchase_note ) {
        ?>
        <tr>
            <td colspan="3" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
                <?php
                echo wp_kses_post( wpautop( do_shortcode( $purchase_note ) ) );
                ?>
            </td>
        </tr>
        <?php
    }
    ?>

<?php endforeach; ?>

Current Outcome:

Current Outcome

Expected Outcome:

Expected Outcome

Anyone can point me in the right direction (I'm a novice)?

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Minhas
  • 93
  • 6
  • Sorry, @LoicTheAztec Now, I edited my original post to add my attempted code and also added the SS of the current preview. – Minhas Feb 03 '21 at 18:59

1 Answers1

2

Here is the correct way to split Emails order items table in 5 columns as follows:

  1. Quantity
  2. Image and product name
  3. SKU
  4. Product Price $45 Excl.Vat $37.5
  5. Line total $90 Excl. Vat $75

Notes:

  • Your templates files are outdated: Below I use last templates for WooCommerce version 3.7+
  • The variation product SKU (if it is set), has the priority over parent the variable product SKU.

The code

• The template file email-order-details.php

<?php
/**
 * Order details table shown in emails.
 *
 * This template can be overridden by copying it to yourtheme/woocommerce/emails/email-order-details.php.
 *
 * HOWEVER, on occasion WooCommerce will need to update template files and you
 * (the theme developer) will need to copy the new files to your theme to
 * maintain compatibility. We try to do this as little as possible, but it does
 * happen. When this occurs the version of the template file will be bumped and
 * the readme will list any important changes.
 *
 * @see https://docs.woocommerce.com/document/template-structure/
 * @package WooCommerce\Templates\Emails
 * @version 3.7.0
 */

defined( 'ABSPATH' ) || exit;

$text_align = is_rtl() ? 'right' : 'left';

do_action( 'woocommerce_email_before_order_table', $order, $sent_to_admin, $plain_text, $email ); ?>

<h2>
    <?php
    if ( $sent_to_admin ) {
        $before = '<a class="link" href="' . esc_url( $order->get_edit_order_url() ) . '">';
        $after  = '</a>';
    } else {
        $before = '';
        $after  = '';
    }
    /* translators: %s: Order ID. */
    echo wp_kses_post( $before . sprintf( __( '[Order #%s]', 'woocommerce' ) . $after . ' (<time datetime="%s">%s</time>)', $order->get_order_number(), $order->get_date_created()->format( 'c' ), wc_format_datetime( $order->get_date_created() ) ) );
    ?>
</h2>

<div style="margin-bottom: 40px;">
    <table class="td" cellspacing="0" cellpadding="6" style="width: 100%; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;" border="1">
        <thead>
            <tr>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Qty.', 'woocommerce' ); ?></th>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Product', 'woocommerce' ); ?></th>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Product code', 'woocommerce' ); ?></th>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Price', 'woocommerce' ); ?></th>
                <th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Total', 'woocommerce' ); ?></th>
            </tr>
        </thead>
        <tbody>
            <?php
            echo wc_get_email_order_items( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
                $order,
                array(
                    'show_sku'      => true,
                    'show_image'    => true,
                    'image_size'    => array( 32, 32 ),
                    'plain_text'    => $plain_text,
                    'sent_to_admin' => $sent_to_admin,
                )
            );
            ?>
        </tbody>
        <tfoot>
            <?php
            $item_totals = $order->get_order_item_totals();

            if ( $item_totals ) {
                $i = 0;
                foreach ( $item_totals as $total ) {
                    $i++;
                    ?>
                    <tr>
                        <th class="td" scope="row" colspan="2" style="text-align:<?php echo esc_attr( $text_align ); ?>; <?php echo ( 1 === $i ) ? 'border-top-width: 4px;' : ''; ?>"><?php echo wp_kses_post( $total['label'] ); ?></th>
                        <td class="td" colspan="3" style="text-align:<?php echo esc_attr( $text_align ); ?>; <?php echo ( 1 === $i ) ? 'border-top-width: 4px;' : ''; ?>"><?php echo wp_kses_post( $total['value'] ); ?></td>
                    </tr>
                    <?php
                }
            }
            if ( $order->get_customer_note() ) {
                ?>
                <tr>
                    <th class="td" scope="row" colspan="2" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Note:', 'woocommerce' ); ?></th>
                    <td class="td" colspan="3" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php echo wp_kses_post( nl2br( wptexturize( $order->get_customer_note() ) ) ); ?></td>
                </tr>
                <?php
            }
            ?>
        </tfoot>
    </table>
</div>

<?php do_action( 'woocommerce_email_after_order_table', $order, $sent_to_admin, $plain_text, $email ); ?>

• Template file email-order-items.php:

<?php
/**
 * Email Order Items
 *
 * This template can be overridden by copying it to yourtheme/woocommerce/emails/email-order-items.php.
 *
 * HOWEVER, on occasion WooCommerce will need to update template files and you
 * (the theme developer) will need to copy the new files to your theme to
 * maintain compatibility. We try to do this as little as possible, but it does
 * happen. When this occurs the version of the template file will be bumped and
 * the readme will list any important changes.
 *
 * @see     https://docs.woocommerce.com/document/template-structure/
 * @package WooCommerce\Templates\Emails
 * @version 3.7.0
 */

defined( 'ABSPATH' ) || exit;

$text_align  = is_rtl() ? 'right' : 'left';
$margin_side = is_rtl() ? 'left' : 'right';

foreach ( $items as $item_id => $item ) :
    $product       = $item->get_product();
    $sku           = '';
    $purchase_note = '';
    $image         = '';

    if ( ! apply_filters( 'woocommerce_order_item_visible', true, $item ) ) {
        continue;
    }

    if ( is_object( $product ) ) {
        $sku           = $product->get_sku();
        $purchase_note = $product->get_purchase_note();
        $image         = $product->get_image( $image_size );
    }

    ?>
    <tr class="<?php echo esc_attr( apply_filters( 'woocommerce_order_item_class', 'order_item', $item, $order ) ); ?>">
        <?php // ----- Item Quantity ----- ?>
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
            <?php
            $qty          = $item->get_quantity();
            $refunded_qty = $order->get_qty_refunded_for_item( $item_id );

            if ( $refunded_qty ) {
                $qty_display = '<del>' . esc_html( $qty ) . '</del> <ins>' . esc_html( $qty - ( $refunded_qty * -1 ) ) . '</ins>';
            } else {
                $qty_display = esc_html( $qty );
            }
            echo wp_kses_post( apply_filters( 'woocommerce_email_order_item_quantity', $qty_display, $item ) );
            ?>
        </td>
        <?php // ----- Product name / image ----- ?>
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align: middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; word-wrap:break-word;">
        <?php

        // Show title/image etc.
        if ( $show_image ) {
            echo wp_kses_post( apply_filters( 'woocommerce_order_item_thumbnail', $image, $item ) );
        }

        // Product name.
        echo '<br>' . wp_kses_post( apply_filters( 'woocommerce_order_item_name', $item->get_name(), $item, false ) );

        // allow other plugins to add additional product information here.
        do_action( 'woocommerce_order_item_meta_start', $item_id, $item, $order, $plain_text );

        wc_display_item_meta( $item, array(
            'label_before' => '<strong class="wc-item-meta-label" style="float: ' . esc_attr( $text_align ) . '; margin-' . esc_attr( $margin_side ) . ': .25em; clear: both">',
        ) );

        // allow other plugins to add additional product information here.
        do_action( 'woocommerce_order_item_meta_end', $item_id, $item, $order, $plain_text );

        ?>
        </td>
        <?php // ----- Product SKU ----- ?>
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align: middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; word-wrap:break-word;">
        <?php
        if ( $show_sku && $sku ) {
            echo wp_kses_post( $sku );
        }
        ?>
        </td>
        <?php // ----- Product Price (Incl & Excl tax) ----- ?>
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
            <?php
                echo wp_kses_post( wc_price( $order->get_item_subtotal( $item, true ), array( 'currency' => $order->get_currency() ) ) ) . '<br>' .
                __("Excl.Vat", "woocommerce") . ': ' . wp_kses_post( wc_price( $order->get_item_subtotal( $item ), array( 'currency' => $order->get_currency() ) ) );
            ?>
        </td>
        <?php // ----- Item Subtotal (Incl & Excl tax) ----- ?>
        <td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
            <?php
                echo wp_kses_post( wc_price( $order->get_line_subtotal( $item, true ), array( 'currency' => $order->get_currency() ) ) ) . '<br>' .
                __("Excl.Vat", "woocommerce") . ': ' . wp_kses_post( wc_price( $order->get_line_subtotal( $item ), array( 'currency' => $order->get_currency() ) ) );
            ?>
        </td>
    </tr>
    <?php

    if ( $show_purchase_note && $purchase_note ) {
        ?>
        <tr>
            <td colspan="3" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
                <?php
                echo wp_kses_post( wpautop( do_shortcode( $purchase_note ) ) );
                ?>
            </td>
        </tr>
        <?php
    }
    ?>

<?php endforeach; ?>

Tested and works as you expect:

enter image description here

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Thank you, Code works like a charm, Is it possible to increase the thumbnail size, as the current thumbnail is too small to notice? – Minhas Feb 04 '21 at 04:43
  • 1
    @Minhas On the template ` email-order-details.php` change the values `32` in `'image_size' => array( 32, 32 ),` to bigger integers values… – LoicTheAztec Feb 04 '21 at 08:45
  • I changed it to 'image_size' => array( 50, 50 ), but no effect. – Minhas Feb 04 '21 at 09:34
  • @Minhas I will look at that later on… – LoicTheAztec Feb 04 '21 at 09:49
  • Thanks! I'm looking forward to hearing back from you. – Minhas Feb 04 '21 at 11:48
  • 1
    @Minhas I have tested changing image size to `'image_size' => array( 50, 50 ),` and it works perfectly for me… see the screenshot [*before*](https://i.stack.imgur.com/3c8gd.png) and [*after*](https://i.stack.imgur.com/6XLXa.png) – LoicTheAztec Feb 04 '21 at 12:15
  • Everything is working fine except for one issue, Product code column is showing the SKU of the main product, is it possible to show SKU on a variation basis too? – Minhas Feb 15 '21 at 15:28
  • I double-check the issue highlighted by you, but each variation has a separate SKU but still now working, strange! – Minhas Feb 15 '21 at 15:41
  • 1
    I already Upvote the answer, Let me know if I have done it in the wrong way or Is there any other way? – Minhas Feb 15 '21 at 16:06
  • Is it possible to do the same but for a total price below the table. To see total price exclude the vat. – Bobi Reshovski Jan 06 '23 at 21:06