DEV Community

WebKoding
WebKoding

Posted on

How I Implemented Inventory Forecasting Algorithms in a WordPress Plugin

When building StockPulse, the core challenge was: how do you calculate reliable inventory forecasts when different products have wildly different sales patterns?

Here's a breakdown of the forecasting engine I built.

Three velocity algorithms, automatic selection

The SP_Velocity_Calculator class implements three approaches:

Simple Moving Average (SMA) — baseline, used when less than 90 days of data exists:

$velocity = array_sum($daily_sales) / count($daily_sales);
Enter fullscreen mode Exit fullscreen mode

Weighted Moving Average (WMA) — recent days get higher weight:

$total_weight = ($n * ($n + 1)) / 2;
$weighted_sum = 0;
foreach ($daily_sales as $i => $sales) {
    $weight = $i + 1;
    $weighted_sum += $sales * $weight;
}
$velocity = $weighted_sum / $total_weight;
Enter fullscreen mode Exit fullscreen mode

Exponential Smoothing — alpha=0.3 by default (configurable per product):

$smoothed = $daily_sales[0];
foreach (array_slice($daily_sales, 1) as $actual) {
    $smoothed = ($alpha * $actual) + ((1 - $alpha) * $smoothed);
}
$velocity = $smoothed;
Enter fullscreen mode Exit fullscreen mode

The engine auto-selects: less than 90 days of history → SMA, 90+ days → WMA.

Reorder point formula

public function calculate_reorder_point(
    float $daily_velocity,
    int $lead_time_days
): float {
    $safety_stock = $daily_velocity * ($lead_time_days * 0.25);
    return ($daily_velocity * $lead_time_days) + $safety_stock;
}
Enter fullscreen mode Exit fullscreen mode

The 25% safety buffer accounts for supplier delays and demand spikes.

Seasonal adjustment

$seasonal_coefficient = $same_period_last_year / $last_month_average;
$adjusted_velocity = $base_velocity * $seasonal_coefficient;
Enter fullscreen mode Exit fullscreen mode

This naturally handles Christmas spikes, summer slowdowns, etc.

HPOS compatibility

WooCommerce's High-Performance Order Storage moved orders to custom tables. Instead of querying wp_posts directly:

$orders = wc_get_orders([
    'status'     => ['wc-completed'],
    'date_after' => date('Y-m-d', strtotime('-90 days')),
    'return'     => 'ids',
    'limit'      => -1,
]);
Enter fullscreen mode Exit fullscreen mode

Caching architecture

All calculations are stored in wp_stockpulse_product_cache with a 24-hour TTL, refreshed daily via WP-Cron.

What's next

v1.1 will add purchase order PDF generator and ABC analysis.

Top comments (0)