Start
On-hand quantity snapshot from ERP (NetSuite)
Open Purchase Orders - quantities and expected delivery dates
OOS risk flags and new demand events
Full model rebuild - velocity recalculation, lead time refresh
Safety stock and reorder point recalculation for all SKUs
ABC reclassification run - SKUs may move class based on last 12 months revenue
Seasonality index recalibration
What do these terms mean?
On-hand: The physical count of units currently sitting in the warehouse, pulled directly from NetSuite.
Open PO: A purchase order already placed with a vendor but not yet received. The model counts this as incoming stock when calculating what to order.
OOS flag: Triggered when available units drop to zero. Urgency escalation starts here.

Why 3 cadences? Daily keeps buyers aware of critical changes overnight. The weekly rebuild ensures all velocity, lead time, and SS formulas use fresh inputs. Monthly recalibrates longer-term classifications (ABC, seasonality) that only need adjusting as trends shift.
Inactive or Discontinued? YES - STOP: excluded entirely from all output
Phase-Out flagged? YES - Set Order Qty = 0, do not reorder; still appears in dashboard
Reactivated SKU? YES - Flag for buyer review; use pre-inactivity velocity as starting point
All checks pass? NO flags - Active SKU, continue to Phase 3
What do these SKU states mean?
Active (no flag): Normal SKU, fully in catalogue, sold regularly. No restriction on ordering.
Inactive / Discontinued: No longer stocked or sold. The model excludes it completely - no order recommendation, no priority score, no dashboard row.
Phase-Out: Still selling through remaining stock but no new orders will be placed. Order Qty is forced to 0. Buyer sees it in the dashboard so they can monitor sell-through.
Reactivated: Was inactive and has been re-added to the catalogue. The model uses its last active velocity as a starting point - flagged because demand may have changed significantly since it was last active.
Trend / RecencyWindow UsedRationale
Growing90-dayRecent demand best predicts future
Stable90-dayConsistent demand, recent window sufficient
Declining (moderate)180-daySmooths short-term drop, avoids over-cutting
Declining (heavy)90-dayUses most recent to reflect current reality
No recent activityLifetime avgSparse sales, uses all available data
No sales at allNoneMovement profile = Dead, routed to Dead Stock status
Multiply by Seasonality Index (based on month-of-year historical pattern)
Apply Event Uplift multiplier if next promo event falls within lead time (capped at +50%)
Key terms explained
Velocity: Average units sold per day. A SKU with velocity = 5 sells roughly 5 units every day.
Window (90d / 180d): The lookback period. 90d = uses only the last 90 days of sales to calculate velocity. 180d = uses the last 6 months.
Trend: Compares recent 90-day sales rate to the prior year. A SKU selling 10/day now vs 6/day a year ago is Growing. One going from 10 to 6 is Declining.

Seasonality Index: A monthly multiplier based on historical patterns. If acupuncture needles historically sell 40% above average in March, the March index is 1.40. Off-peak months are below 1.0.
Event Uplift: When a Listrek promotional event falls within the lead time window, velocity is increased by the SKU's historical event uplift percentage. Capped at 50% to prevent runaway overordering.
Adjusted Velocity: The window velocity above, after Seasonality Index and Event Uplift are applied. This is the single number used everywhere downstream - Days Cover, Reorder Point, Order Quantity, and the status assignment all use this same adjusted velocity, never the raw window velocity.
Tier 1 SKU actual median - uses any available PO history for this specific SKU - most accurate
Tier 2 Vendor actual - median lead time across all SKUs from the same vendor
Tier 3 Item master default lead time (as set in NetSuite for this item)
Tier 3.5 Category median - used when item master equals the 15-day default (avoids circular fallback)
Tier 4 15-day system default (last resort fallback)
Vendor Unreliable: std dev of delivery time >= 14 days - vendor timing is unpredictable
Why a waterfall, not a simple lookup?
Lead Time: Days between placing a purchase order and receiving the goods in the warehouse. Crucial - if you underestimate it, you run out before the order arrives.
Why Tier 1 is best: It uses this exact SKU's own history with this exact vendor. Vendor-SKU combinations often have quirks that a generic average misses.
Tier 2 (Vendor actual): If no SKU-specific data exists, the model looks at all POs from that vendor across all their SKUs. Still real delivery data, just less specific.
Tier 3.5 edge case: When the NetSuite item master says "15 days" (which is also the system default), the model can't tell if that was manually set or just inherited. It falls back to the category median instead.

Vendor Unreliable flag: If a vendor's std dev is 14+ days, they might deliver 2 weeks early or 2 weeks late. The model flags this so buyers know the lead time estimate is unreliable and extra buffer may be needed.
SS = Z x (Monthly_StdDev / sqrt(30)) x sqrt(Lead_Time_Days)
ROP = (Velocity x Lead_Time_Days) + Safety_Stock
SKU ClassMethodZ-ScoreService Level
A-classStatistical1.6595%
B-classStatistical1.6595%
C-classQuarterly Reviewn/aReview cycle
Promotional event within lead time window: increase ROP by event demand estimate
How the formula works in practice
Safety Stock: Extra units held as a buffer against unexpected demand spikes or late deliveries. Think of it as insurance stock.
Z = 1.65: A value from the statistics normal distribution table. Z = 1.65 means: if you hold SS = 1.65 x daily StdDev x sqrt(LT), you will avoid a stockout 95% of the time. Only 5 months in 100 would see a stockout despite holding SS.
Monthly StdDev / sqrt(30): Converts monthly sales variability into a daily rate. A SKU with monthly StdDev = 30 units has daily StdDev = 30 / 5.48 = 5.5 units/day.
ROP: The inventory level at which you trigger the next purchase order. Below this = order now.

Real example - Blue Poppy Sports Oil (BP.T.CHSMO):
Inputs Monthly StdDev = 28.25 units
Lead Time = 23 days
Velocity = 4.54 units/day
Safety Stock calculation Daily StdDev = 28.25 / sqrt(30) = 5.16
SS = 1.65 x 5.16 x sqrt(23)
SS = 1.65 x 5.16 x 4.80 = 40.8 ~ 41 units
Reorder Point ROP = (4.54 x 23) + 41
ROP = 104.4 + 41 = 145 units
If available stock drops to 145 units, place a reorder. Safety buffer of 41 units absorbs demand spikes during the 23-day lead time.
Movement profile = Dead (zero velocity)
DEAD STOCK
Available = 0
OUT OF STOCK
Available < Safety Stock
BELOW SAFETY STOCK
Available < Reorder Point
REORDER NOW
Movement profile = Slow or Very Slow (low but real demand)
SLOW MOVING
Days Cover > 365
EXCESS STOCK (>365D)
Days Cover > 250
EXCESS STOCK (>250D)
Everything else (adequate cover, not at reorder point, not excess)
HEALTHY
What each status means for the buyer
DEAD STOCK: Zero recent movement. Checked before every other rule - never worth an action flag. Flag for clearance, write-off, or phase-out decision.
OUT OF STOCK: Zero available units. Every day without stock is lost sales and lost margin. Order immediately - no delay justified.
BELOW SAFETY STOCK: Units exist but you have dipped below your safety buffer. You are burning through insurance stock. Order urgently.
REORDER NOW: Stock has reached the planned trigger point. Place order as scheduled - normal urgency, not emergency.
SLOW MOVING: Low but real demand, not yet triggering an action bucket. Monitor; avoid over-ordering.
EXCESS STOCK (>365D): Over a year of cover. Capital is tied up. Do not reorder. Consider promotions or return to vendor.
EXCESS STOCK (>250D): Between 250 and 365 days of cover. Capital building up - watch for further slowdown before it becomes severe excess.
HEALTHY: Good coverage with no action needed this cycle. If an open PO already covers a low-stock SKU's need, it still shows its true status here - check the PO Status/Purchase Qty columns in SKU Master, which reflect coverage directly instead of overriding the status.
A-class: 7-day review cycle | B-class: 14-day | C-class: 21-day
Fast-moving (>1/day): +10% | Medium (0.1-1/day): +20% | Slow (<0.1/day): +30%
Recommended Qty = Safety Stock
+ MIN(CEIL(Velocity x (Lead_Time + Review_Cycle) x Buffer), CEIL(Velocity x 180))
- Available
Only computed for Out of Stock / Below Safety Stock / Reorder Now - every other status is 0
Purchase Qty = ROUND_UP_TO_PAX(MAX(0, Recommended Qty - Open_PO_Qty))
Recommended Qty gated to the 3 action statuses only - Dead Stock, Slow Moving, Healthy, and both Excess Stock tiers always show 0
Velocity target capped at 180 days of cover
Purchase Qty rounds up to the nearest pack size (pax); shows "-" if the open PO already covers the full Recommended Qty
Breaking down the formula
Why two stages? Recommended Qty always shows the true target position, regardless of what's already inbound - so a buyer can see the real need at a glance. Purchase Qty then nets that against any open PO, so it's the actual number to put on a new order.
Safety Stock in the target: Recommended Qty always includes Safety Stock as a floor. This guarantees every SKU flagged Out of Stock, Below Safety Stock, or Reorder Now gets a nonzero recommendation - closing a gap where urgent SKUs used to show no purchase suggestion at all.
Review Cycle: How often you look at each class. A-class SKUs (high value) are reviewed every 7 days. You are ordering to cover Lead Time + Review Cycle days of demand.
Movement Profile: How fast a SKU sells. Fast = over 1 unit/day. Medium = 0.1-1/day. Slow = under 0.1/day. Slower movers get a larger buffer because their demand is harder to predict.
Buffer: The multiplier above 1.0 adds a safety margin. Medium = 1.20 means you order 20% more than the bare coverage window to absorb demand variance.
Netting open POs: Already-ordered stock is subtracted in Stage 2 only, so it never hides the true target computed in Stage 1.

Real example - Carbon Needles .20x25 (CB5.20x25):
Inputs Class: B | Velocity: 4.3/day | Movement: Fast
Lead Time: 15 days | Safety Stock: 267 units
Available: 158 units | Open PO: 0 units | Pack size: 100
Stage 1 - Recommended Qty Review Cycle (B) = 14 days | Buffer (Fast) = 1.10
Target = CEIL(4.3 x (15 + 14) x 1.10) = CEIL(137.2) = 138
Recommended Qty = 267 + 138 - 158 = 247 units
Stage 2 - Purchase Qty Raw = MAX(0, 247 - 0) = 247
Rounded up to nearest 100 = 300 units
Recommended Qty shows 247 (the true target). Purchase Qty shows 300 - the number to actually place on the next PO.
ComponentMax PtsDetail
Urgency (Status)40OOS=40 | Below SS=30 | Reorder Now=20
Margin at Risk30(Margin at Risk / highest MAR in current dataset) x 30
ABC Class20A=20 | B=12 | C=5
Days Cover100d=10 | <7d=8 | <14d=6 | <30d=4
Score 85+
Order today
Score 60-84
Order this cycle
Score 35-59
Watch - monitor
How to read the priority score
Why a composite score? When 600 SKUs all need reordering, buyers need a ranked list. The score combines urgency + financial impact + strategic class + time pressure into one number.
Urgency (40 pts): Status alone drives the top of the score. An OOS A-class SKU scores at least 60 pts (40 urgency + 20 ABC) before MAR and days cover are even counted.
Margin at Risk (MAR$): Estimated gross profit at stake while the SKU is out or running low. Formula: daily gross margin x days exposed. Normalized against the highest MAR in the current dataset run - so a 30-point score means you have the highest financial exposure in today's list.
Dynamic denominator: The MAR normalization uses the current dataset's maximum, not a fixed number. This means scores shift slightly each run as the mix of at-risk SKUs changes.

Score 85+: Drop everything, order today. Most likely OOS or Below SS with high margin exposure.
Score 60-84: Handle in this buying cycle, don't skip it.
Score 35-59: Watch list - not urgent but keep an eye on it.
SKUs classified as Strong Seasonality or Moderate Seasonality only
Days of cover > days remaining in season AND fewer than 90 days remain in season?
Flag: Overstock Risk - reduce order quantity or defer to next season
What triggers the overstock check
Why only seasonal SKUs? A non-seasonal SKU with 200 days of cover is fine - it will sell through eventually. For a seasonal SKU, 200 days of cover in November means the stock will still be sitting there when the season ends and demand drops to near zero.
The 90-day window: The check activates when fewer than 90 days remain in the peak season. Before that, there is still enough time to sell through normally.
Seasonality classification: SKUs are classified as Strong Seasonality, Moderate Seasonality, Mild Seasonality, or Flat based on the month-over-month demand pattern across prior years.

Example: A winter heating pad SKU with 200 days of cover, checked in mid-November with 60 days left in the winter season. 200 > 60 and fewer than 90 days remain - Overstock Risk flag is set. Recommendation: do not order, or reduce qty significantly.
2 or more OOS events in the past 12 months? YES - Flag as Chronic OOS
Demand Spike / Planning Gap - recent velocity significantly exceeds historical model
Vendor Unreliable - high lead time variance (std dev >= 14 days) flagged in Phase 4
Insufficient PO History - too few POs to build a reliable Tier 1 lead time estimate
Safety Stock Too Low - service level may need review for this SKU
Chronic OOS vs. a one-off stockout
OOS event definition: Available = 0 during a period when the SKU historically sells at normal levels. A naturally slow month doesn't count - the context must show active demand.
Why 2+ events = Chronic? One stockout can be bad luck. Two or more in 12 months means the planning model is systematically failing for this SKU - something structural needs fixing.

Root cause matters for the fix:
Demand Spike: The model underestimated demand. Review velocity inputs and safety stock level.
Vendor Unreliable: Lead time variance too high. Talk to the vendor or qualify an alternate source.
Insufficient PO History: Lead time is falling back to Tier 3 or 4 (less accurate). Place more orders or update item master.
Safety Stock Too Low: Z-score or formula inputs need recalibration for this SKU's demand profile.
Priority Score (0-100) with action threshold band
Recommended Qty (full target incl. Safety Stock) and Purchase Qty (net of open PO, rounded to pack size)
Margin at Risk ($) - potential gross margin loss from the current stock position
Plain-English Recommendation - generated from status, score and flags
DEAD STOCK
OUT OF STOCK
BELOW SAFETY STOCK
REORDER NOW
SLOW MOVING
EXCESS (>365D)
EXCESS (>250D)
HEALTHY
What the buyer actually sees
Each SKU row in the dashboard shows its status badge, priority score, order qty, and a one-line recommendation. The buyer works down the sorted list by priority score, highest first.

Example recommendations:
OOS for 2 days. A-class. Order 108 units.
$3,200 margin at risk. Priority: 91.
Reorder point reached. A-class, fast moving.
Order 167 units. Priority: 72.
Healthy - 83 days of cover.
No action needed. Priority: 0.

Dashboard tab assignment: The tab a SKU lands in equals its status. Buyers typically start with the OOS tab (most urgent), then Below Safety Stock, then Reorder Now. Healthy and Excess are informational. If an open PO already covers a low-stock SKU's need, the status itself does not change - check the PO Status and Purchase Qty columns in SKU Master, which show coverage directly instead of masking the true stock position.
END - Output Delivered to Dashboard