Simple PHP Table Total - php

I've been trying for ages to simply calculate the "weight" field we have added to an invoice plugin called sliced invoices. All I need is for it to total the weight based on the value in the table.
The weight field has the div class, "adjust". Here's a link to the forms PDF and you'll see the empty table field - Weight. http://cavcon.co.za/sliced_quote/346-2/?create=pdf&id=346&print_pdf=8d39bfe988
Here's the code I'm working with:
<?php
$count = 0;
$items = sliced_get_invoice_line_items(); // gets quote and invoice
if( !empty( $items ) ) :
foreach ( $items[0] as $item ) {
$class = ($count % 2 == 0) ? 'even' : 'odd';
$item_tax = isset( $item['tax'] ) ? $item['tax'] : 0;
$line_total = $shared->get_line_item_sub_total( $item['qty'], $item['amount']);
?>
<tr class="row_<?php echo $class; ?> sliced-item">
<td class="qty"><?php echo esc_html( $item['qty'] ); ?></td>
<td class="service"><?php echo esc_html( isset( $item['title'] ) ? $item['title'] : '' ); ?>
<?php if ( isset( $item['description'] ) ) : ?>
<br/><span class="description"><?php echo esc_html( $item['description']); ?></span>
<?php endif; ?>
</td>
<td class="rate"><?php echo esc_html( $shared->get_formatted_currency( $item['amount'] ) ); ?></td>
<?php if ( sliced_hide_adjust_field() === false) { ?>
<td class="adjust"><?php echo esc_html( $item_tax ? $item['tax'] . 'kg' : '-' ); ?></td>
<?php } ?>
<td class="total"><?php echo esc_html( $shared->get_formatted_currency( $line_total ) ); ?></td>
</tr>
<?php $count++;
}
endif; ?>
</tbody>
</table>
<?php
}
endif;
if ( ! function_exists( 'sliced_display_invoice_totals' ) ) :
function sliced_display_invoice_totals() { ?>
<table class="table table-sm table-bordered" id="new-table">
<tbody>
<?php do_action( 'sliced_invoice_before_totals' ); ?>
<tr class="row-sub-total">
<td class="rate"><?php echo _e( 'Total Weight', 'sliced-invoices' ); ?></td>
<td class="total-weight"><?php echo esc_html( $item['weight'] ); ?>
</td>
</tr>
<tr class="row-sub-total">
<td class="rate"><?php echo _e( 'Sub Total', 'sliced-invoices' ); ?></td>
<td class="total"><?php echo esc_html( sliced_get_invoice_sub_total() ); ?></td>
</tr>
<tr class="row-tax">
<td class="rate"><?php echo esc_html( sliced_get_tax_name() ); ?></td>
<td class="total"><?php echo esc_html( sliced_get_invoice_tax() ); ?></td>
</tr>
<tr class="table-active row-total">
<td class="rate"><strong><?php echo _e( 'Total', 'sliced-invoices' ); ?></strong></td>
<td class="total"><strong><?php echo esc_html( sliced_get_invoice_total() ); ?></strong></td>
</tr>
<?php do_action( 'sliced_invoice_after_totals' ); ?>
</tbody>
</table>
<?php
}
endif;
THIS IS WHERE I'M AT
if ( ! function_exists( 'sliced_display_invoice_totals' ) ) :
$total_weight = 0;
foreach ( $items[0] as $item ) {
$total_weight = $total_weight + $item['tax'];
}
function sliced_display_invoice_totals() { ?>
<table class="table table-sm table-bordered" id="new-table">
<tbody>
<?php do_action( 'sliced_invoice_before_totals' ); ?>
<tr class="row-sub-total">
<td class="rate"><?php echo _e( 'Total Weight', 'sliced-
invoices' ); ?></td>
<td class="total-weight"><?php echo esc_html(
$total_weight . 'kg') ?>
</td>

If you run PHP version 5.5 or above, you can sum the weights in this way:
if( !empty( $items ) ) :
foreach ( $items[0] as $item ) {
(...)
}
$total_weight = array_sum( array_column( $items, 'tax' ) );
endif; ?>
Otherwise, you have to increment $total_weight inside foreach loop:
$total_weight = 0;
foreach ( $items[0] as $item ) {
$total_weight = $total_weight + $item['tax'];
(...)
}
If you output total weight in the same scope of above script, simply put echo $total_weight; where you want to print it (or echo esc_html( $total_weight . 'kg');, according with your other prints).
But maybe your totals is output through sliced_display_invoice_totals function (although I don't see any call to this function in your code); in this case, the $total_weight variable is not directly available in the function, due to different scope. You have to print it in this way:
function sliced_display_invoice_totals() { ?>
(...)
echo $GLOBALS['total_weight']; // or echo esc_html( $GLOBALS['total_weight'] . 'kg');
(...)
}
Pleae careful note:
In your code, the function sliced_display_invoice_totals declaration is prepended by condition if ( ! function_exists( 'sliced_display_invoice_totals' ) ): this means that, if the function is already defined, your function modification will be ignored: in this case, you have to modify the already declare function instead.
To see if the function is already declared, place this temporary code before if condition:
if( function_exists( 'sliced_display_invoice_totals' ) )
{
$reflFunc = new ReflectionFunction( 'sliced_display_invoice_totals' );
die( $reflFunc->getFileName() . ':' . $reflFunc->getStartLine() );
}
In this way you can see the filePath and line in which the function is defined and modify it.
See more about array_sum
See more about array_column
See more about Variables scope
See more about Reflection class

Here is a list of things to change:
1. Unpaired HTML tags
The code in your question does not include the <table> nor <tbody> tags which you later close. I assume they are present in your original code.
2. Unpaired braces
Maybe also just an error while drafting your question, but after the </table> there is a closing PHP } and endif which have no matching opening statements.
3. Use of _e
According to the documentation of _e() on wordpress.org:
Displays the returned translated text from translate()
[...]
This function does not return a value.
So you are not supposed to write echo _e(...), you should just write _e(...) instead.
4. Function sliced_display_invoice_totals
You define this function that displays the totals, but you never call it. If you intend to display that table then do just that, without function definition.
5. When $items is empty
You have a condition so that the first table is only produced when $items is not empty. However, the totals table is produced outside that context, so it would also be produced when $items is empty. This might not be what you want. Consider moving the code for the second table inside that if block.
6. Mixed use of normal and alternative syntax
You use often the : ... endif syntax, which can indeed be helpful to read code that is interrupted by HTML code. But you don't use it consistently. I suggest using it for all if statements and for the foreach construct as well.
7. Calculate and display the $total_weight
Inside your existing foreach loop, add the weight of each line to $total_weight. You must also initialise this value just before starting the loop with 0.
Then in the second table display that $total_weight with the same format as you display the individual weights in the first table.
Here is the code that has all of the above applied:
<table class="table table-sm table-bordered" id="detail-table">
<tbody>
<?php
$count = 0;
$items = sliced_get_invoice_line_items(); // gets quote and invoice
// *** add this line ***
$total_weight = 0;
if( !empty( $items ) ) :
foreach ( $items[0] as $item ) :
$class = ($count % 2 == 0) ? 'even' : 'odd';
$item_tax = isset( $item['tax'] ) ? $item['tax'] : 0;
// *** add this line ***
$total_weight += $item_tax;
$line_total = $shared->get_line_item_sub_total( $item['qty'], $item['amount']);
?>
<tr class="row_<?php echo $class; ?> sliced-item">
<td class="qty"><?php echo esc_html( $item['qty'] ); ?></td>
<td class="service"><?php echo esc_html( isset( $item['title'] ) ? $item['title'] : '' ); ?>
<?php if ( isset( $item['description'] ) ) :
?> <br/><span class="description"><?php echo esc_html( $item['description']); ?></span>
<?php endif;
?> </td>
<td class="rate"><?php echo esc_html( $shared->get_formatted_currency( $item['amount'] ) ); ?></td>
<?php if ( sliced_hide_adjust_field() === false) :
?> <td class="adjust"><?php echo esc_html( $item_tax ? $item['tax'] . 'kg' : '-' ); ?></td>
<?php endif;
?>
<td class="total"><?php echo esc_html( $shared->get_formatted_currency( $line_total ) ); ?></td>
</tr>
<?php $count++;
endforeach; // use consistent syntax (alternative)
?>
</tbody>
</table>
<?php
// **** Remove the IF and the FUNCTION. You want to execute this now. ***
?>
<table class="table table-sm table-bordered" id="new-table">
<tbody>
<?php do_action( 'sliced_invoice_before_totals' ); ?>
<tr class="row-sub-total">
<td class="rate"><?php _e( 'Total Weight', 'sliced-invoices' ); ?></td>
<td class="total-weight"><?php echo esc_html( $total_weight ? $total_weight . 'kg' : '-' ); ?></td>
</tr>
<tr class="row-sub-total">
<td class="rate"><?php _e( 'Sub Total', 'sliced-invoices' ); ?></td>
<td class="total"><?php echo esc_html( sliced_get_invoice_sub_total() ); ?></td>
</tr>
<tr class="row-tax">
<td class="rate"><?php echo esc_html( sliced_get_tax_name() ); ?></td>
<td class="total"><?php echo esc_html( sliced_get_invoice_tax() ); ?></td>
</tr>
<tr class="table-active row-total">
<td class="rate"><strong><?php _e( 'Total', 'sliced-invoices' ); ?></strong></td>
<td class="total"><strong><?php echo esc_html( sliced_get_invoice_total() ); ?></strong></td>
</tr>
<?php do_action( 'sliced_invoice_after_totals' ); ?>
</tbody>
</table>
<?php
// **** Consider closing the if (!empty($items)) here ***
endif;
?>
You did not specify the code for the many functions you call, like sliced_get_tax_name, sliced_get_invoice_tax,... etc. Unless there is a problem with any of these functions, the above code should work.

Related

Customize WooCommerce attribute table to two columns

I'm looking for some help getting customize the attribute table on WooCommerce.
<table>
<?php foreach (array_chunk($product_attributes, 2) as $product_attribute_key => $product_attribute) :{ ?>
<tr class="woocommerce-product-attributes-item woocommerce-product-attributes-item--<?php echo esc_attr( $product_attribute_key ); ?>">
<?php foreach ($product_attribute as $value) :{ ?>
<th class="woocommerce-product-attributes-item__label"><?php echo wp_kses_post( $value['label'] ); ?></th>
<td class="woocommerce-product-attributes-item__value"><?php echo wp_kses_post( $value['value'] ); ?></td>
<?php } endforeach; ?>
</tr>
<?php } endforeach; ?>
</table>
I tried modify the above snippet in order to break line between Attribute Name and Attribute Value to display the attributes like it is in the below picture.
attributes
Is there any easy hook to get this work?
Thanks in Advance.
Create child-theme in case of theme update to not lose your settings.
Create in child-theme folders /woocommerce/single-product/ and copy from woocommerce/templates/single-product the file product-attributes.php.
Change:
defined( 'ABSPATH' ) || exit;
if ( ! $product_attributes ) {
return;
}
?>
<table class="woocommerce-product-attributes shop_attributes">
<?php foreach ( $product_attributes as $product_attribute_key => $product_attribute ) : ?>
<tr class="woocommerce-product-attributes-item woocommerce-product-attributes-item--<?php echo esc_attr( $product_attribute_key ); ?>">
<th class="woocommerce-product-attributes-item__label"><?php echo wp_kses_post( $product_attribute['label'] ); ?></th>
<td class="woocommerce-product-attributes-item__value"><?php echo wp_kses_post( $product_attribute['value'] ); ?></td>
</tr>
<?php endforeach; ?>
</table>
to
defined( 'ABSPATH' ) || exit;
if ( ! $product_attributes ) {
return;
}
$i = 0;
?>
<div class="woocommerce-product-attributes shop_attributes">
<?php foreach ( $product_attributes as $product_attribute_key => $product_attribute ) : ?>
<div class="woocommerce-product-attributes-item woocommerce-product-attributes-item--<?php echo esc_attr( $product_attribute_key ); ?>">
<div class="woocommerce-product-attributes-item__label"><?php echo wp_kses_post( $product_attribute['label'] ); ?></div>
<div class="woocommerce-product-attributes-item__value"><?php echo wp_kses_post( $product_attribute['value'] ); ?></div>
</div>
<?php
if ($i % 2 != 0){
echo '<div class="clear"></div>';
}
$i++;
endforeach; ?>
</div>
add CSS
/*2 Columnt style on product attributes*/
.woocommerce-product-attributes.shop_attributes .woocommerce-product-attributes-item {
float: left;
width: 50%;
margin-bottom: 8px;
padding-right:10px;
}

How to localize Woocommerce weight

So, in settings of Woocommerce weight and other metric system in Russian language.
But in product page for example it still in English (g - gramm).
I have copied file in to my theme: /wp-content/themes/my_theme/woocommerce/single-product/product-attributes.php
What should I do in this file to display right Weight metric system in Russian language? This code did not help:
<td class="product_weight"><?php echo wc_format_localized_decimal( $product->get_weight() ) . ' ' . __(esc_attr( get_option( 'woocommerce_weight_unit' ) ), 'woocommerce'); ?></td>
Code of /product-attributes.php:
defined( 'ABSPATH' ) || exit;
if ( ! $product_attributes ) {
return;
}
?>
<table class="woocommerce-product-attributes shop_attributes">
<?php foreach ( $product_attributes as $product_attribute_key => $product_attribute ) : ?>
<tr class="woocommerce-product-attributes-item woocommerce-product-attributes-item--<?php echo esc_attr( $product_attribute_key ); ?>">
<th class="woocommerce-product-attributes-item__label"><?php echo wp_kses_post( $product_attribute['label'] ); ?></th>
<td class="woocommerce-product-attributes-item__value"><?php echo wp_kses_post( $product_attribute['value'] ); ?></td>
</tr>
<?php endforeach; ?>
</table>
Thank you!
Done, just posted this code in theme's functions.php
function localize_weight_units($weight) {
return str_replace('g', 'г', $weight);
}
add_filter('woocommerce_format_weight', 'localize_weight_units');

Add total discount coupons amount in Woocommerce checkout

I am trying to add up all of my added coupons to get a discount total in the checkout. I tried adding a variable at the top of the checkout template file and doing ++ for each entry but I throws errors.
Any ideas how to add the values to a variable to get a total?
The checkout totals regenerate if you alter a value so I found my answer being outputted each time the loop runs.
My code:
<?php foreach ( WC()->cart->get_coupons() as $code => $coupon ) : ?>
<tr class="cart-discount coupon-<?php echo esc_attr( sanitize_title( $code ) ); ?>">
<th><?php wc_cart_totals_coupon_label( $coupon ); ?></th>
<td><?php $helloworld = wc_cart_totals_coupon_html( $coupon )++; ?></td>
</tr>
<?php endforeach; ?>
This can be done easily using some existing WC_Cart methods.
So in the template checkout/oreder-review.php, just after this:
<?php foreach ( WC()->cart->get_coupons() as $code => $coupon ) : ?>
<tr class="cart-discount coupon-<?php echo esc_attr( sanitize_title( $code ) ); ?>">
<th><?php wc_cart_totals_coupon_label( $coupon ); ?></th>
<td><?php wc_cart_totals_coupon_html( $coupon ); ?></td>
</tr>
<?php endforeach; ?>
You will insert the following code (after line 69):
<?php
$discount_excl_tax_total = WC()->cart->get_cart_discount_total();
$discount_tax_total = WC()->cart->get_cart_discount_tax_total();
$discount_total = $discount_excl_tax_total + $discount_tax_total;
if( ! empty($discount_total) ): ?>
<tr class="cart-discount coupon-<?php echo esc_attr( sanitize_title( $code ) ); ?>">
<th><?php _e('Discount total','woocommerce'); ?></th>
<td><?php echo wc_price(-$discount_total) ?></td>
</tr>
<?php endif; ?>
Tested and works.

Grouped product parent vs Grouped product child in woocommerce

I am using PDF packing plugin (WooCommerce PDF Invoices) to create packing slips after order is placed. And Product Bundles plugin for bundled products.
In those PDFs I want to differentiate between the container of a bundled product and its child.
Currently I am using this from Product Bundles:
if(wc_pb_is_bundle_container_cart_item($item) )
it checks if an item is container of a bundled product then returns true. I need a similar function which would return true if the item is child or is inside a bundle.
here is the code of packing slip pdf body:
<?php
/**
* PDF packing slip template body.
*
* This template can be overridden by copying it to youruploadsfolder/woocommerce-pdf-invoices/templates/packing-slip/simple/yourtemplatename/body.php.
*
* HOWEVER, on occasion WooCommerce PDF Invoices 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.
*
* #author Bas Elbers
* #package WooCommerce_PDF_Invoices/Templates
* #version 0.0.1
*/
$templater = WPI()->templater();
$order = $templater->order;
$formatted_shipping_address = $order->get_formatted_shipping_address();
$formatted_billing_address = $order->get_formatted_billing_address();
$line_items = $order->get_items( 'line_item' );
$color = $templater->get_option( 'bewpi_color_theme' );
?>
<table>
<tr class="title">
<td colspan="3">
<h2><?php _e( 'Packing Slip', 'woocommerce-pdf-invoices' ); ?></h2>
</td>
</tr>
<tr class="information">
<td width="50%">
<?php echo nl2br( $templater->get_option( 'bewpi_company_address' ) ); ?>
</td>
<td>
<?php
if ( $templater->get_option( 'bewpi_show_ship_to' ) && ! empty( $formatted_shipping_address ) && $formatted_shipping_address !== $formatted_billing_address && ! $templater->has_only_virtual_products( $line_items ) ) {
printf( '<strong>%s</strong><br />', __( 'Ship to:', 'woocommerce-pdf-invoices' ) );
echo $formatted_shipping_address;
}
?>
</td>
<td>
<?php echo $formatted_billing_address; ?>
</td>
</tr>
</table>
<table>
<thead>
<tr class="heading" bgcolor="<?php echo $color; ?>;">
<th>
<?php _e( 'Qty', 'woocommerce-pdf-invoices' ); ?>
</th>
<th>
<?php _e( 'Product', 'woocommerce-pdf-invoices' ); ?>
</th>
<th>
<?php _e( 'SKU', 'woocommerce-pdf-invoices' ); ?>
</th>
</tr>
</thead>
<tbody>
<?php
//$parentItem = 0;
foreach ( $line_items as $item_id => $item ) {
$product = BEWPI_WC_Order_Compatibility::get_product( $order, $item );
if(wc_pb_is_bundle_container_cart_item($item) ){
?>
<tr class="item">
<td width="10%">
<?php echo $item['qty']; ?>
<?php // print_r($item); die(); ?>
</td>
<td width="65%">
<?php
echo $item['name'];
do_action( 'woocommerce_order_item_meta_start', $item_id, $item, $order );
$templater->wc_display_item_meta( $item, true );
$templater->wc_display_item_downloads( $item, true );
do_action( 'woocommerce_order_item_meta_end', $item_id, $item, $order );
?>
</td>
<td width="25%">
<?php echo $product && $product->get_sku() ? $product->get_sku() : '-'; ?>
</td>
</tr>
<?php } else { ?>
<tr class="item">
<td width="10%" style="float:right;">
<?php echo $item['qty']; ?>
</td>
<td width="5%" style="float:right;">
<?php
echo '----'.$item['name'];
//echo 'i m child';
do_action( 'woocommerce_order_item_meta_start', $item_id, $item, $order );
$templater->wc_display_item_meta( $item, true );
$templater->wc_display_item_downloads( $item, true );
do_action( 'woocommerce_order_item_meta_end', $item_id, $item, $order );
?>
</td>
<td width="25%" style="float:right;">
<?php echo $product && $product->get_sku() ? $product->get_sku() : '-'; ?>
</td>
</tr>
<?php }} ?>
</tbody>
</table>
<table class="notes">
<tr>
<td>
<?php
// Customer notes.
if ( $templater->get_option( 'bewpi_show_customer_notes' ) ) {
// Note added by customer.
$customer_note = BEWPI_WC_Order_Compatibility::get_customer_note( $order );
if ( $customer_note ) {
printf( '<strong>' . __( 'Note from customer: %s', 'woocommerce-pdf-invoices' ) . '</strong><br />', nl2br( $customer_note ) );
}
// Notes added by administrator on 'Edit Order' page.
foreach ( $order->get_customer_order_notes() as $custom_order_note ) {
printf( '<strong>' . __( 'Note to customer: %s', 'woocommerce-pdf-invoices' ) . '</strong><br />', nl2br( $custom_order_note->comment_content ) );
}
}
?>
</td>
</tr>
</table>
I want to append ---- infront of the name of product only if the product is part of any bundle. just want this check, I cant figure out how can I do that
Thanks.
Issue is that you are using Cart Functions instead of Order Functions.
First you need to use wc_pb_is_bundle_container_order_item instead of wc_pb_is_bundle_container_cart_item as we are checking against Order Items.
Further to that wc_pb_is_bundled_order_item is the check you should be using to determine if the product is a child item of an container item. This is important because it will check if item has a parent container so your code can scale when you also have non-bundled items in order as well.
Below is just partial relevant code from question.
<?php } else { ?>
<tr class="item">
<td width="10%" style="float:right;">
<?php echo $item['qty']; ?>
</td>
<td width="5%" style="float:right;">
<?php
// Adding a check to see if current item is a child of a container
$item_name_padding = wc_pb_is_bundled_order_item( $item ) ? '----': '';
echo $item_name_padding . $item['name'];
//echo 'i m child';
do_action( 'woocommerce_order_item_meta_start', $item_id, $item, $order );
$templater->wc_display_item_meta( $item, true );
$templater->wc_display_item_downloads( $item, true );
do_action( 'woocommerce_order_item_meta_end', $item_id, $item, $order );
?>
</td>
<td width="25%" style="float:right;">
<?php echo $product && $product->get_sku() ? $product->get_sku() : '-'; ?>
</td>
</tr>
<?php }} ?>

WooCommerce - Renaming and using renamed order status

I've already renamed my order status 'completed' to 'paid' using this code
function wc_renaming_order_status( $order_statuses ) {
foreach ( $order_statuses as $key => $status ) {
$new_order_statuses[ $key ] = $status;
if ( 'wc-completed' === $key ) {
$order_statuses['wc-completed'] = _x( 'Paid', 'Order status', 'woocommerce' );
}
}
return $order_statuses;
}
add_filter( 'wc_order_statuses', 'wc_renaming_order_status' );
My problem is that I did a page template with a list of all my orders:
<?php
while ( $loop->have_posts() ) : $loop->the_post();
$order_id = $loop->post->ID;
$order = new WC_Order($order_id);
?>
<tr>
<td style="text-align:left;"><?php echo $order->get_order_number(); ?></td>
<td style="text-align:left;"><?php echo $order->billing_first_name; ?>
<?php echo $order->billing_last_name; ?></td>
<td style="text-align:left;"><?php echo $order->billing_company; ?></td>
<td style="text-align:left;"><?php echo $order->status; ?></td>
</tr>
<?php endwhile; ?>
And the $order->status still returns 'completed' instead of 'paid'.
How can I solve this problem?
Thanks
This is normal and your this specific case you could use some additional code, creating a function to display your custom renamed status:
function custom_status($order){
if($order->status == 'completed')
return _x( 'Paid', 'woocommerce' );
else
return $order->status;
}
This code goes in function.php file of your active child theme (or theme) or also in any plugin file.
In your template page you will use it this way:
<?php
while ( $loop->have_posts() ) : $loop->the_post();
$order_id = $loop->post->ID;
$order = new WC_Order($order_id);
?>
<tr>
<td style="text-align:left;"><?php echo $order->get_order_number(); ?></td>
<td style="text-align:left;"><?php echo $order->billing_first_name; ?>
<?php echo $order->billing_last_name; ?></td>
<td style="text-align:left;"><?php echo $order->billing_company; ?></td>
<td style="text-align:left;"><?php echo custom_status($order); ?></td>
</tr>
<?php endwhile; ?>
This code is tested and works.
Reference: Renaming WooCommerce Order Status

Categories