Open post

PHP snippet to calculate reference number for Finnish invoices

(Finnish summary: PHP aliohjelma, jolla voi laskea laskun viitenumeron laskun numerosta.)

I’m not sure how many countries are using so called 7-3-1 method to generate reference numbers for Invoices, but here in Finland it’s a common practise. Looks like that Estonians are using the same.

Anyway, I needed to create a PHP code to generate reference numbers. I found nice information here: https://wiki.xmldation.com/support/fk/finnish_reference_number

I though that someone else may one day need same functionality, so here’s the snippet. It’s a PHP function that takes invoice number and adds a check digit at the end. And that’s, there’s your reference number. Have fun 🙂

   private function calcInvoiceRefNum($invoice_num) {
        $digits = str_split($invoice_num);
        $method = [7,3,1];
        $mi = 0; // $method array index
        $sum = 0;
        
        $index = count($digits);
        
        // Walk through all digits in reverse order and sum up
        while ($index) {
            --$index;
            
            $sum += $digits[$index] * $method[$mi];
            
            $mi++;
            if ($mi > 2) $mi = 0;
        }
        
        // Find the next zero ending number from $sum
        $nearest_10 = ceil($sum / 10) * 10;
        
        // Add the checksum to the end of the invoice number
        $last_digit = $nearest_10 - $sum;
        if ($last_digit == 10)
            $last_digit = 0;
        
        return $invoice_num.$last_digit;
    }
Open post

WP_Query: Showing two set of results with different sort order in one view

In my client project I have defined custom field “_featnumber” (aka “meta key”) for “product” post type. The key indicates if a post is “featured” or not.

The _featnumber key can have any integer value starting from 1, which is used to set the position in the view. However, typically the key is set empty. In those cases I have a piece of code that remove the key entirely from the database.

This type of SQL query is pretty easy if you simply want to show those posts that contain the key:

        $args = array(
            'post_type' => 'product',
            'posts_per_page' => 16,
            'meta_key' => '_featnumber',
            'orderby' => 'meta_value',
            'order' => 'ASC',
        );
        
        $loop = new WP_Query($args);

This will generate “INNER JOIN” SQL query (joining a post and the key), which will ensure that only those posts are returned which has _featnumber key set.

However, the requirement is to show 16 posts all times. If there are less (or none!) featured items, the view should be filled with other posts – ordered descending by published date. So, we should first show featured posts in ascending order and then other posts in descending order.

Such a SQL query is possible to create but it’s not straight forward, since WordPress does not natively support handling two set of queries with different ORDER BY clause. I did try that, and it worked, but it required using a filter and manually entering ORDER BY statement. I also had a gut feeling that the performance of the query was not best possible.

I ended up thing of having two simple queries and merging them afterwards. It took only some minutes to figure out how to do that and the end result was perfect!

Find the working code below. First we grab max 16 products with _featnumber. In the second round we query all products that don’t have _featnumber and in ‘posts_per_page’ ensure that the total number of results will be 16 (‘posts_per_page’ => 16 – $loop1->post_count).

Finally we merge the arrrays and set a correct “post_count” for the final set. Note that for merging we need to create an empty WP_Query object.

That’s it! Now you can use $loop array in a normal way, have a “while ($loop->have_posts()” loop and use all WordPress functions to grab the content. Enjoy 😉

        $args1 = array(
            'post_type' => 'product',
            'posts_per_page' => 16,
            'meta_key' => '_featnumber',
            'orderby' => 'meta_value',
            'order' => 'ASC'
        );
        
        $loop1 = new WP_Query($args1);

        $args2 = array(
            'post_type' => 'product',
            'posts_per_page' => 16 - $loop1->post_count,
            'meta_query' => array(
                array(
                 'key' => '_featnumber',
                 'compare' => 'NOT EXISTS'
                ),
            )
        );
        
        $loop2 = new WP_Query($args2);
        
        $loop = new WP_Query();
        $loop->posts = array_merge($loop1->posts, $loop2->posts);
        $loop->post_count = $loop1->post_count + $loop2->post_count;

Adding multiselect custom field in Woocommerce product form

Here’s a quick tip if you are wondering of how to add multiselect fields in your Woocommerce product form.

Woocommerce has a great support for adding custom fields on a product page. There are some plugins available which help you to get things done, but it’s not really that complicated to write the code by yourself – and eliminate a need to install yet another plugin for your site. Here’s a great article of how to add fields: Mastering WooCommerce Products Custom Fields.

What if you want to add a multiselect field? It’s not that difficult either – create a function to populate multiselect field and save selection when product page is saved. The key difference with a single select field is that instead of saving single value to WordPress meta data, you save an array of values.

First, creating function to output and populate multiselect field, we can simply copy Woocommerce function for single select field and modify that to our needs. You can find it in woocommerce/includes/admin/wc-mate-box-function.php file. To save your time, here’s a modified function. Include it to your functions.php file:

function woocommerce_wp_select_multiple( $field ) {
    global $thepostid, $post;

    $thepostid              = empty( $thepostid ) ? $post->ID : $thepostid;
    $field['class']         = isset( $field['class'] ) ? $field['class'] : 'select short';
    $field['wrapper_class'] = isset( $field['wrapper_class'] ) ? $field['wrapper_class'] : '';
    $field['name']          = isset( $field['name'] ) ? $field['name'] : $field['id'];
    $field['value']         = isset( $field['value'] ) ? $field['value'] : ( get_post_meta( $thepostid, $field['id'], true ) ? get_post_meta( $thepostid, $field['id'], true ) : array() );

    echo '<p class="form-field ' . esc_attr( $field['id'] ) . '_field ' . esc_attr( $field['wrapper_class'] ) . '"><label for="' . esc_attr( $field['id'] ) . '">' . wp_kses_post( $field['label'] ) . '</label><select id="' . esc_attr( $field['id'] ) . '" name="' . esc_attr( $field['name'] ) . '" class="' . esc_attr( $field['class'] ) . '" multiple="multiple">';

    foreach ( $field['options'] as $key => $value ) {
        echo '<option value="' . esc_attr( $key ) . '" ' . ( in_array( $key, $field['value'] ) ? 'selected="selected"' : '' ) . '>' . esc_html( $value ) . '</option>';
    }

    echo '</select> ';

    if ( ! empty( $field['description'] ) ) {
        if ( isset( $field['desc_tip'] ) && false !== $field['desc_tip'] ) {
            echo '<img class="help_tip" data-tip="' . esc_attr( $field['description'] ) . '" src="' . esc_url( WC()->plugin_url() ) . '/assets/images/help.png" height="16" width="16" />';
        } else {
            echo '<span class="description">' . wp_kses_post( $field['description'] ) . '</span>';
        }
    }
    echo '</p>';
}

At this point you probably have a function in functions.php that handles adding your custom fields to the product form. Place the following call there:

woocommerce_wp_select_multiple(
    array(
        'id' => '_myfield',
        'name' => '_myfield[]',
        'label' => 'My Multiselect Field',
        'options' => ['key1' => 'First Item', 'key2' => 'Second Item', 'key3' => 'Third Item']
    )
);

Last but not least, place the following line to a function where you save your fields:

update_post_meta($post_id, '_myfield', $_POST['_myfield']);

See, you don’t even have to serialize the array data, good old WordPress does that for you inside update_post_meta function.

That should be enough. This was a very simplified example and you may need to add some validation logic and other stuff based on your needs.

Hello Slide Panel!

MojoPlug Slide Panel version 1.0.0 is published!

The starting point for the plugin came from a website project for a book publisher. There were some analysis telling that most of the current website visitors were using mobile browsers – a well known trend today. So I started to think of how to fit content to a limited space with intuitive user interface.

Slide panels looked really tempting. But after trying out some free plugins, I coudn’t find exactly what I was looking for – a simple and clean panel with smooth animations. The performance seemed to a problem especially with mobile browsers. After studying the issue I found out that there in fact is a simple technique that uses device’s hardware acceleration when available. Bingo!

You can download the plugin free from this site or from the WordPress plugin directory.

Enjoy! And read more about the MojoPlug Slide Panel.