Seasonal Inventory Forecasting: From Historical Data to Smart Reorder Decisions

Learn how to build a seasonal demand forecasting system that transforms historical sales data into actionable reorder recommendations using NetSuite data and Python analytics.

Inventory Management, Data Analytics, NetSuite

Jan 12, 2026

Inventory management is one of the most consequential challenges in supply chain operations. Order too much and you tie up working capital in slow-moving stock, incur storage costs, and risk obsolescence. Order too little and you face stockouts that cost sales, damage customer relationships, and create expediting expenses that erode margins.

The solution lies in demand forecasting that accounts for seasonality, giving you data-driven reorder recommendations rather than gut instincts or outdated static reorder points.

This comprehensive guide walks through building a seasonal forecasting and reorder system using historical sales data from NetSuite, Python analysis, and the anchor-based methodology that ensures smooth transitions from actual to forecasted data.

The Business Case for Seasonal Forecasting

Traditional reorder point calculations assume consistent demand throughout the year. The classic formula of Reorder Point = (Daily Demand × Lead Time) + Safety Stock treats every day as identical. But for most businesses, demand fluctuates dramatically with the calendar.

Consider a business selling outdoor equipment: summer months might see 200% of average demand for camping gear, while winter drops to 40% of average. A static reorder point calculated on annual averages fails catastrophically in both directions. You'll stockout during peak season when customers are ready to buy, and overstocked during off-season when capital should be deployed elsewhere.

The Inventory Optimization Opportunity

Research from McKinsey & Company found that AI-driven demand forecasting can reduce lost sales due to stockouts by up to 65% while simultaneously decreasing inventory holding costs by 35%. Even simpler statistical approaches that incorporate seasonality deliver substantial improvements over flat averages.

The financial impact compounds in several ways:

  • Reduced Carrying Costs - Inventory carrying costs typically run 20-30% of inventory value annually, including storage, insurance, obsolescence, and opportunity cost of capital

  • Fewer Stockouts - Lost sales from stockouts often exceed the product margin because customers may switch to competitors permanently

  • Better Cash Flow - Right-sized inventory frees working capital for growth investments, marketing, or debt reduction

  • Operational Efficiency - Predictable ordering patterns reduce expediting costs and enable better supplier negotiations

The 80/20 Reality of Inventory

Not all inventory deserves equal analytical attention. The Pareto Principle applies directly to inventory management: roughly 20% of your SKUs typically account for 80% of your value. This is the foundation of ABC inventory classification, a methodology that dates back to General Electric in the 1950s but remains essential today:

  • Class A Items - The vital few. High value, often lower quantity. These require tight inventory control, accurate demand forecasting, frequent monitoring, and sophisticated replenishment strategies. Typically 10-20% of SKUs representing 70-80% of value

  • Class B Items - The important many. Moderate value and quantity. Balanced approach with medium oversight and periodic review. Typically 20-30% of SKUs representing 15-25% of value

  • Class C Items - The trivial many. Low individual value, high quantity. Simpler replenishment strategies, bulk ordering, and higher safety stock percentages acceptable. Typically 50-60% of SKUs representing 5-10% of value

The methodology in this guide can be applied to any inventory segment, but you'll see the greatest ROI by starting with your Class A items where forecast accuracy directly impacts the bottom line. A 10% improvement in forecast accuracy for Class A items might be worth more than 50% improvement for Class C items.

Understanding the Forecasting Methodology

Our approach uses seasonal decomposition with an anchor-based transition. This methodology combines time-tested statistical techniques with practical considerations for business forecasting. Here's what each component means:

Seasonal Decomposition Explained

Time series data can be decomposed into several components:

  • Trend - The long-term direction of the data (growth or decline over years)

  • Seasonality - Regular, predictable patterns that repeat on a fixed schedule (monthly, quarterly, annually)

  • Cyclical - Longer-term fluctuations without fixed periodicity (economic cycles, industry trends)

  • Residual/Noise - Random variation that cannot be attributed to the above components

For inventory forecasting, we focus primarily on seasonality. By analyzing 2+ years of historical sales, we identify monthly patterns. The calculation derives seasonal factors by comparing each month's average sales to the overall average.

A seasonal factor of 1.2 means that month typically sees 20% higher sales than the annual average. A factor of 0.8 means 20% lower. These factors become multipliers for projecting future demand.

The Anchor-Based Transition

A common problem with forecasting is the visual and mathematical disconnect between historical actuals and future projections. Charts show a solid line of history that suddenly jumps to a dashed forecast line, often at a different level.

The anchor-based approach solves this by:

  1. Using the most recent actual data point as the anchor

  2. Ensuring the forecast starts from this anchor point

  3. Applying seasonal factors to project forward while maintaining continuity

This creates both visual smoothness and mathematical consistency. Decision-makers can trust that the forecast logically extends from known data rather than appearing disconnected.

Item-Level Application

We calculate seasonal factors at the aggregate level (inventory class or category) where we have sufficient data for statistical reliability, then apply those factors to individual item run rates.

For example: If an item sold 120 units last year (10 units/month average) and January has a seasonal factor of 1.5, we forecast 15 units for January. This approach gives us the statistical power of aggregate analysis with the granularity needed for operational decisions.

Data Extraction from NetSuite

The foundation of accurate forecasting is clean, comprehensive historical data. NetSuite stores transactional data in normalized tables that we query using SuiteQL. Here are the three queries needed to extract the required information.

Query 1: Sales History by Month

This query retrieves monthly aggregated sales volume for your target inventory class. The aggregation to monthly level smooths out daily and weekly noise while preserving seasonal patterns.

SELECT
    TO_CHAR(t.trandate, 'YYYY-MM') as sales_month,
    NVL(BUILTIN.DF(i.class), 'Unclassified') as class_name,
    SUM(ABS(tl.quantity)) as units_sold
FROM transaction t
JOIN transactionline tl ON t.id = tl.transaction
JOIN item i ON tl.item = i.id
WHERE t.type IN ('CustInvc', 'CashSale')
  AND tl.mainline = 'F'
  AND i.itemtype IN ('InvtPart', 'Assembly')
  AND t.trandate >= TO_DATE('2023-01-01', 'YYYY-MM-DD')
  AND i.class = 1  -- Update for different inventory classes
GROUP BY TO_CHAR(t.trandate, 'YYYY-MM'), BUILTIN.DF(i.class)
ORDER BY sales_month ASC

Key implementation notes:

  • Include at least 24 months of history - Two full years provides enough data to identify reliable seasonal patterns while being recent enough to reflect current business conditions

  • Filter by inventory class ID - Segment your analysis by business-meaningful groupings. Different product categories often have different seasonal patterns

  • Use ABS() on quantity - This handles any negative values from returns or credit memos, ensuring consistent volume measurements

  • Include both invoice types - CustInvc (invoices) and CashSale cover the primary revenue transaction types in most NetSuite implementations

Query 2: Current Inventory Status

This query retrieves current stock levels and item details for all active items in your target class.

SELECT
    i.id,
    i.itemid,
    i.displayname,
    NVL(i.totalquantityonhand, 0) as qty_on_hand,
    NVL(i.reorderpoint, 0) as reorder_point,
    NVL(i.preferredstocklevel, 0) as preferred_stock_level,
    NVL(i.averagecost, 0) as avg_cost,
    NVL(i.purchasedescription, i.displayname) as purchase_description
FROM Item i
WHERE i.class = 1  -- Match the class from Query 1
  AND i.isinactive = 'F'
  AND i.itemtype IN ('InvtPart', 'Assembly')
ORDER BY i.itemid ASC

The current inventory position is critical for calculating net requirements. Items with high on-hand quantities may not need reordering even if forecasted demand is substantial.

Query 3: Item Run-Rate (Previous Year Sales)

This query establishes baseline volume by retrieving the previous calendar year's total sales per item.

SELECT
    tl.item,
    i.itemid,
    SUM(ABS(tl.quantity)) as units_sold_last_year,
    COUNT(DISTINCT TO_CHAR(t.trandate, 'YYYY-MM')) as months_with_sales
FROM transaction t
JOIN transactionline tl ON t.id = tl.transaction
JOIN item i ON tl.item = i.id
WHERE t.trandate >= TO_DATE('2025-01-01', 'YYYY-MM-DD')
  AND t.trandate <= TO_DATE('2025-12-31', 'YYYY-MM-DD')
  AND t.type IN ('CustInvc', 'CashSale')
  AND tl.mainline = 'F'
  AND i.class = 1  -- Match the class from Query 1
GROUP BY tl.item, i.itemid
ORDER BY units_sold_last_year DESC

The months_with_sales field helps identify items with intermittent demand patterns that may require different forecasting approaches.

Python Analysis Implementation

With the data extracted, we apply the seasonal forecasting methodology in Python. The following code demonstrates the complete analysis pipeline.

Step 1: Calculate Seasonal Factors

import pandas as pd
import numpy as np

# Load and prepare sales history
df_hist = pd.read_csv('sales_history.csv')
df_hist['sales_month'] = pd.to_datetime(df_hist['sales_month'])
df_hist['month'] = df_hist['sales_month'].dt.month
df_hist['year'] = df_hist['sales_month'].dt.year

# Calculate overall average monthly sales
overall_avg = df_hist['units_sold'].mean()

# Calculate seasonal factors for each calendar month
monthly_avg = df_hist.groupby('month')['units_sold'].mean()
seasonal_factors = monthly_avg / overall_avg

# Display the seasonal pattern
print("Seasonal Factors by Month:")
print("-" * 30)
for month, factor in seasonal_factors.items():
    month_name = pd.Timestamp(f'2026-{month:02d}-01').strftime('%B')
    direction = "above" if factor > 1 else "below"
    pct = abs(factor - 1) * 100
    print(f"{month_name}: {factor:.3f} ({pct:.1f}% {direction} average)")

The output might look like:

Seasonal Factors by Month:
------------------------------
January: 0.892 (10.8% below average)
February: 0.845 (15.5% below average)
March: 0.923 (7.7% below average)
April: 1.056 (5.6% above average)
May: 1.134 (13.4% above average)
June: 1.178 (17.8% above average)
July: 1.089 (8.9% above average)
August: 0.967 (3.3% below average)
September: 0.912 (8.8% below average)
October: 1.045 (4.5% above average)
November: 1.234 (23.4% above average)
December: 1.156 (15.6% above average)

Step 2: Apply Factors to Item-Level Forecasts

# Load inventory and run-rate data
df_inventory = pd.read_csv('inventory_status.csv')
df_run_rate = pd.read_csv('item_run_rate.csv')

# Merge datasets on item ID
merged = pd.merge(
    df_inventory,
    df_run_rate,
    left_on='id',
    right_on='item',
    how='left'
)

# Fill missing run rates with 0 (new items with no history)
merged['units_sold_last_year'] = merged['units_sold_last_year'].fillna(0)

# Calculate base monthly demand from last year's actuals
merged['avg_monthly_demand'] = merged['units_sold_last_year'] / 12.0

# Generate forecasts for each month using seasonal factors
for month in range(1, 13):
    col_name = f'forecast_m{month:02d}'
    merged[col_name] = merged['avg_monthly_demand'] * seasonal_factors[month]

# Calculate quarterly forecasts
merged['forecast_q1'] = merged[['forecast_m01', 'forecast_m02', 'forecast_m03']].sum(axis=1)
merged['forecast_q2'] = merged[['forecast_m04', 'forecast_m05', 'forecast_m06']].sum(axis=1)
merged['forecast_q3'] = merged[['forecast_m07', 'forecast_m08', 'forecast_m09']].sum(axis=1)
merged['forecast_q4'] = merged[['forecast_m10', 'forecast_m11', 'forecast_m12']].sum(axis=1)
merged['forecast_annual'] = merged['forecast_q1'] + merged['forecast_q2'] + merged['forecast_q3'] + merged['forecast_q4']

Step 3: Calculate Reorder Recommendations

# Define planning horizon (e.g., next quarter)
planning_horizon = 'forecast_q1'

# Calculate net requirements
merged['projected_demand'] = merged[planning_horizon]
merged['net_need'] = merged['projected_demand'] - merged['qty_on_hand']

# Apply safety stock (e.g., 20% buffer for Class A items)
safety_stock_pct = 0.20
merged['safety_stock'] = merged['projected_demand'] * safety_stock_pct
merged['gross_requirement'] = merged['projected_demand'] + merged['safety_stock']

# Calculate reorder quantity (never negative)
merged['reorder_qty'] = (merged['gross_requirement'] - merged['qty_on_hand']).clip(lower=0)

# Flag items needing reorder
merged['needs_reorder'] = merged['reorder_qty'] > 0

# Calculate reorder value
merged['reorder_value'] = merged['reorder_qty'] * merged['avg_cost']

# Sort by reorder value for prioritization
recommendations = merged[merged['needs_reorder']].sort_values(
    'reorder_value',
    ascending=False
)

print(f"\nReorder Recommendations Summary:")
print(f"  Items requiring reorder: {len(recommendations)}")
print(f"  Total reorder quantity: {recommendations['reorder_qty'].sum():,.0f} units")
print(f"  Total reorder value: ${recommendations['reorder_value'].sum():,.2f}")

Visualizing the Forecast

Effective visualization is critical for communicating forecast insights to stakeholders. The chart below shows Class A inventory sales history alongside the 2026 forecast with confidence intervals.

<embed type="graph" src="https://res.cloudinary.com/drnuzed5h/raw/upload/v1768221992/nsgpt/blog/forecasting-and-reorder/class_a_forecast_chart.json">

Reading the visualization:

  • Blue line - Historical actual sales from 2024-2025, showing the real demand patterns your business experienced

  • Green marker - Current month anchor point (January 2026 actuals) that connects history to forecast

  • Dashed green line - Forecasted demand through December 2026, derived from seasonal factors applied to baseline demand

  • Shaded area - 50% confidence interval around the forecast, representing expected variation based on historical variance

The chart clearly shows the seasonal pattern: lower demand in late winter (February-March), building through spring and early summer, a slight summer dip, then the major Q4 peak (November-December).

The Reorder Recommendations Output

The final output is a reorder recommendation table that compares forecasted demand against current stock. This table becomes your actionable procurement guide.

<embed type="table" src="https://res.cloudinary.com/drnuzed5h/raw/upload/v1768221992/nsgpt/blog/forecasting-and-reorder/reorder_recommendations.csv">

Interpreting the recommendations:

  • Items with positive Recommended Reorder - Current stock is insufficient to meet forecasted demand plus safety stock for the planning horizon

  • Items with zero Recommended Reorder - Current stock exceeds requirements; no immediate action needed

  • Reorder Value column - Helps prioritize which purchase orders to process first based on dollar impact

Incorporating Lead Time Into Planning

Effective reorder planning must account for supplier lead times. The order trigger should occur early enough that replenishment arrives before stock runs out.

The traditional reorder point formula is:

Reorder Point = (Daily Demand × Lead Time Days) + Safety Stock

For seasonal businesses, this formula needs modification:

Seasonal Reorder Point = (Seasonally-Adjusted Daily Demand × Lead Time Days) + Safety Stock

When lead times vary by supplier, build that variability into your safety stock calculations. If a supplier's lead time ranges from 14-21 days, plan for the longer scenario or increase safety stock to cover the potential gap.

Handling Lead Time Variability

Lead time variability often causes more stockouts than demand variability. Consider tracking and analyzing actual vs. quoted lead times by supplier:

# Calculate lead time safety stock
avg_lead_time = 14  # days
lead_time_std = 3   # days standard deviation
service_level_z = 1.65  # 95% service level

# Lead time safety stock component
avg_daily_demand = merged['avg_monthly_demand'] / 30
lead_time_safety = service_level_z * avg_daily_demand * lead_time_std

merged['adjusted_safety_stock'] = merged['safety_stock'] + lead_time_safety

Advanced Considerations

New Product Forecasting

Items without historical data require different approaches:

  • Analogous item method - Use the seasonal pattern of a similar existing item as a proxy

  • Category average method - Apply the category-level seasonal factors with conservative volume estimates

  • Judgmental adjustment - Start with category patterns but adjust based on marketing plans, competitive positioning, and launch timing

Handling Promotional Periods

Promotions create demand spikes that can distort seasonal factors if not handled properly:

  • Identify and flag promotional periods in your historical data

  • Calculate seasonal factors both with and without promotional periods

  • Use promotion-adjusted factors for baseline planning, but add back expected promotional lift when promotions are planned

Multi-Location Considerations

If your business operates across multiple warehouses or locations:

  • Seasonal patterns may differ by geography (regional climate, local events)

  • Calculate location-specific seasonal factors when possible

  • Consider transfer possibilities between locations before triggering new purchases

Repeatable Workflow Summary

This methodology is designed to be repeatable for different inventory segments. Here's the step-by-step workflow:

Step 1: Define Scope
Identify the Class, Department, or Location ID you want to analyze. Start with your highest-value segment for maximum impact.

Step 2: Extract Data
Run the three SuiteQL queries, updating the filter conditions for your target segment. Export results to CSV format for Python processing.

Step 3: Calculate Factors
Execute the Python analysis to calculate seasonal factors from the historical data. Review the factors for reasonableness before proceeding.

Step 4: Generate Forecasts
Apply seasonal factors to item-level run rates to create monthly and quarterly forecasts.

Step 5: Calculate Requirements
Compare forecasted demand against current inventory positions to generate net requirements and reorder recommendations.

Step 6: Validate and Adjust
Review the Recommended Reorder column against your knowledge of unusual circumstances: new product launches, discontinued items, known supply constraints, or upcoming promotions.

Step 7: Execute
Convert validated recommendations into purchase orders, adjusting for minimum order quantities, supplier terms, and consolidation opportunities.

Best Practices for Ongoing Accuracy

A forecast is only valuable when maintained and validated. Here are essential practices to ensure ongoing accuracy:

  • Monthly Reconciliation - Compare forecasted vs. actual demand each month to identify systematic over or under-prediction. Track forecast accuracy metrics like MAPE (Mean Absolute Percentage Error)

  • Annual Factor Refresh - Recalculate seasonal factors annually as business patterns evolve. Major shifts in product mix or channel strategy may warrant more frequent updates

  • Outlier Management - Flag and investigate months with unusual activity (major promotions, supply disruptions, one-time large orders) before they skew your seasonal factors. Consider excluding true outliers from factor calculations

  • Cross-Functional Alignment - Share forecasts with sales, marketing, and finance to incorporate known upcoming events: campaigns, product launches, market changes, or customer-specific intelligence

  • Safety Stock Tuning - Adjust safety stock levels based on actual lead time variability, service level requirements, and stockout cost analysis. Higher-margin items may justify higher safety stock

  • Supplier Collaboration - Share forecasts with key suppliers to improve their planning and potentially negotiate better terms or shorter lead times

Measuring Success

Track these metrics to evaluate your forecasting and inventory management improvement:

  • Inventory Turns - Cost of Goods Sold / Average Inventory Value. Higher is better, but balance against service levels

  • Fill Rate - Percentage of orders shipped complete from stock. Target 95%+ for Class A items

  • Stockout Rate - Percentage of SKUs with zero on-hand. Target less than 2% for active items

  • Forecast Accuracy (MAPE) - Average of |Actual - Forecast| / Actual. Target less than 25% for monthly forecasts

  • Days of Supply - On-Hand Inventory / Average Daily Demand. Should align with lead time plus safety stock days

Next Steps

  • Run the SuiteQL queries against your NetSuite instance to extract historical data

  • Start with your highest-value inventory class (Class A items) for maximum impact

  • Compare the model's Q1 recommendations against your current ordering patterns

  • Establish baseline metrics before implementation to measure improvement

  • Extend the methodology to other inventory segments once validated

  • Consider integrating with automated purchase order generation for faster execution

  • Explore demand sensing approaches that incorporate leading indicators beyond historical patterns

Seasonal inventory forecasting transforms procurement from reactive to proactive. By understanding your historical patterns and applying them systematically, you reduce both stockouts and excess inventory while freeing up working capital for growth. The methodology presented here provides a practical, repeatable framework that works with data you already have in NetSuite.

The best forecasting system is one that gets used consistently. Start simple with the core seasonal decomposition approach, validate it against your business knowledge, then enhance with the advanced considerations as your confidence grows.