# Month-to-Date Sales vs Last Month Widget

> Adds a dashboard widget comparing this month's revenue and order count so far with the same days of last month, with the change in percent.

- Type: PHP
- Runs: Admin only
- Category: [WooCommerce](https://wpide.com/library/category/woocommerce/)
- Requires: WooCommerce
- Tags: woocommerce, dashboard, reports, sales
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/woo-dashboard-month-sales/

## Code

```php
add_action( 'wp_dashboard_setup', function () {
    if ( ! current_user_can( 'view_woocommerce_reports' ) ) {
        return;
    }

    wp_add_dashboard_widget( 'wpide_month_sales', 'Sales this month', function () {
        global $wpdb;

        $data = get_transient( 'wpide_month_sales' );

        if ( false === $data ) {
            $tz        = wp_timezone();
            $now       = new DateTimeImmutable( 'now', $tz );
            $this_from = $now->modify( 'first day of this month midnight' );
            $last_from = $this_from->modify( '-1 month' );
            $last_to   = $last_from->add( $this_from->diff( $now ) );
            $hpos      = class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();

            $sum = function ( $from, $to ) use ( $wpdb, $hpos ) {
                $from = $from->setTimezone( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d H:i:s' );
                $to   = $to->setTimezone( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d H:i:s' );

                if ( $hpos ) {
                    return $wpdb->get_row( $wpdb->prepare( "SELECT COUNT(*) AS orders, COALESCE(SUM(total_amount),0) AS total FROM {$wpdb->prefix}wc_orders WHERE type = 'shop_order' AND status IN ('wc-processing','wc-completed') AND date_created_gmt BETWEEN %s AND %s", $from, $to ) );
                }

                return $wpdb->get_row( $wpdb->prepare( "SELECT COUNT(*) AS orders, COALESCE(SUM(m.meta_value),0) AS total FROM {$wpdb->posts} p INNER JOIN {$wpdb->postmeta} m ON m.post_id = p.ID AND m.meta_key = '_order_total' WHERE p.post_type = 'shop_order' AND p.post_status IN ('wc-processing','wc-completed') AND p.post_date_gmt BETWEEN %s AND %s", $from, $to ) );
            };

            $data = array( 'this' => (array) $sum( $this_from, $now ), 'last' => (array) $sum( $last_from, $last_to ) );
            set_transient( 'wpide_month_sales', $data, 30 * MINUTE_IN_SECONDS );
        }

        $change = (float) $data['last']['total'] > 0 ? ( (float) $data['this']['total'] - (float) $data['last']['total'] ) / (float) $data['last']['total'] * 100 : null;

        printf(
            '<p style="font-size:24px;margin:.2em 0"><strong>%1$s</strong></p><p>%2$s</p><p>%3$s</p>',
            wp_kses_post( wc_price( $data['this']['total'] ) ),
            esc_html( sprintf( '%d orders so far this month', $data['this']['orders'] ) ),
            null === $change
                ? esc_html( 'No sales in the same period last month.' )
                : wp_kses_post( sprintf( '<span style="color:%1$s">%2$s%3$s%%</span> compared with the same days last month (%4$s).', $change >= 0 ? '#008a20' : '#d63638', $change >= 0 ? '+' : '', number_format_i18n( $change, 1 ), wc_price( $data['last']['total'] ) ) )
        );
    } );
} );
```

## Notes

Counts processing and completed orders, caches the figures for 30 minutes and works with HPOS and legacy order storage.

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Month-to-Date Sales vs Last Month Widget" in its snippet library and add it in one click; or create a new PHP snippet in WPIDE (or any code snippets plugin) and paste the code above. More snippets: https://wpide.com/library/
