Retail and e-commerce · Inventory and stockouts

Inventory and coverage

How much of each product is actually available at each branch, DC or marketplace warehouse, how many days it lasts, and which items are heading for a stockout.

UsageCommon
EffortMedium
ForRetailers, distributors and sellers with inventory across several branches, DCs or marketplace warehouses.
Source

Inventory position from ERP, e-commerce and marketplaces, plus invoiced sales

Rule

Net available, single product key, average sales and stockout parameters

Outcome

Days of coverage and stockout status by product and branch

Activation

Replenishment dashboard, availability AI and alerts

What it solves

  • Available to sell is what is left after reservations, without doubling inventory or counting unlimited stock.
  • Each product has days of coverage and a single status: stockout, critical, attention, healthy, excess or no turnover.
  • The time series keeps the days at zero, which is what really measures stockouts and turnover.

Business questions it answers

Ask your agent these questions once the case is live.

Is product X available? At which branch or DC?
Which A-curve products are out of stock or have coverage below the replenishment lead time?
How many days of coverage do we have per channel, counting what is on the way?
How much did we fail to sell because of stockouts this month?
Which items are in excess or have no turnover, and what are they worth at cost?
What needs to be bought now, considering open requisitions and purchase orders?

What you need

  • ERP, e-commerce and marketplaces connected to Nekt, with inventory position and invoiced sales
  • The branch register, including which branches share the same physical inventory
  • Replenishment lead time by product or group, and target stock in days

What you get

  • A current position table by product and branch with total, reserved, available and in transit
  • A history with two measurements per day, including zero balances
  • Stockout traffic light with ABC curve, coverage and estimated lost sales
  • Freshness monitor and a catalog exceptions table

Where to activate

Stockout and replenishment dashboard

Traffic light by product, branch and channel, daily stockout trend and net requirement by curve.

Availability AI agent

A salesperson asks by product name and gets available stock by branch, with the time of the latest measurement.

Alerts

An A-curve item that went into stockout or critical, and an inventory load that passed the limit without updating.

Ready to build?

The current position comes out fast. The product key across sources, mirror branches and the stateful history are what take time. The detailed view has the business rules, the pitfalls and the acceptance test. Or send everything straight to your agent with the Nekt MCP.

The problem

Each source names inventory its own way (total, reserved, committed, available, sellable, inbound) and identifies the product another way: a code with leading zeros in the ERP, a SKU with color and size embedded in e-commerce, a SKU with a suffix in the marketplace warehouse. Mirror branches double the balance and unlimited stock sentinels inflate the total. The current position has no past: without storing the series, there is no way to measure how many days the item sat at zero.

How it is usually done, and the rule

Each row is a mistake that already happened in practice.

The common wayThe rule
01
A single "inventory" column that is sometimes total and sometimes available.
Separate the four quantities

Store them in their own columns: total, reserved (committed, orders already sold and not shipped), available = greater of (total minus reserved) and zero, and in transit (incoming, inbound, being restocked). In Shopify, read the quantities by name (available, committed, incoming). In the Amazon warehouse, available is fulfillableQuantity and in transit is the sum of inbound working, shipped and receiving. Never mix in transit with available.

02
Stripping leading zeros on one side and keeping them on the other, or extracting the code from the slug.
One canonical product key

Standardize the code before joining inventory with sales and the catalog: left-pad with zeros to the ERP length (18 positions in SAP; 6 in Protheus, only for 4 to 6 digit codes), never applied to marketplace SKUs. Extract the code from the product reference field, not from the URL slug. When the SKU embeds color and size, extract product and color with a documented expression and aggregate without size if sales are compared at that level. Kits get their own prefix (P for product, K for kit) so they do not collide with products.

03
Discarding every SKU that does not match the catalog directly.
Channel SKU mapped in cascade

A marketplace SKU resolves to the product in order: alias table, direct code, and a regular expression for warehouse suffixes (for example, the FBA suffix). Store which rule resolved it. An unmapped SKU with stock stays visible as "unmapped"; an unmapped SKU with zero (inactive listing) leaves the dashboard.

04
Adding a channel's sentinel and showing years of coverage.
Unlimited and sentinel stock stay out

Products flagged with unlimited stock (hasunlimitedquantity true) leave the balance. Some marketplaces return a huge fixed quantity as an unlimited sentinel (around 10,000 or more): flag it and exclude it from the sum, with the threshold validated with the client. Mirror branches of the same physical inventory aggregate by stock group, as in the AI assistant.

05
Average sales from orders, counting cancellations and shipments.
Average sales come from sales that move inventory

Average daily sales use line items from non-cancelled invoices whose operation moves inventory: outbound adds and returns (inbound) subtract; operations that do not move inventory stay out. 30-day window for the average; keep 400 days of sales to compare year over year. When the channel provides sales ready-made (unitsShippedT30 in the Amazon warehouse), use the channel's figure.

06
Dividing only available by sales and treating zero sales as zero coverage.
Coverage counts what is on the way

Days of coverage = (available + in transit) / 30-day average daily sales. In transit goes in the numerator: for a channel that uses the marketplace warehouse, ignoring it underestimates days of coverage. With no sales in 30 days, coverage is null and the item goes to the no-turnover status, never to infinity or zero.

07
Different thresholds in the dashboard, the alert and the AI agent.
Traffic light with parameters in one place

Active assortment = invoiced in the last 90 days, not inactive and sales not suspended. Otherwise: out of assortment. Within it: stockout (zero available), critical (coverage below the product's replenishment lead time, with the group's lead time as fallback), attention (below lead time plus a margin), healthy, excess (above target stock in days) and no turnover. All thresholds live in a parameters table or block, never scattered across the query.

08
A fixed curve from the start of the year applied forever.
Rolling ABC curve by revenue

Rank products by revenue over the last 90 days and accumulate: up to 80% of revenue is A, up to 95% is B, the rest is C. Recompute every day on the rolling window. A product with no sales in the window has no curve ("SEM_CURVA"), not C.

09
A history table with only products that have a positive balance.
History requires state, and zero goes in

A SQL query in Nekt is stateless: to build a series, use a notebook that appends the snapshot. Two measurements per day (opening in the morning and closing at night, Brasília time), classified by the hour before or after noon so it survives schedule changes. Store zero balances. A daily step after the close purges beyond retention and leaves one row per product, branch, day and measurement. Without a snapshot, rebuild the day from the inventory ledger: the day's last movement gives the balance and days without movement inherit the previous one.

10
Calculating purchases from the balance alone, ignoring purchase orders already issued.
Net requirement and lost sales

Net requirement = open requisition minus (available + open purchase order), by product and curve. Estimated lost sales = average daily sales x days out of stock in the period x average price, only for active assortment items. Inventory value = quantity x book cost. A product with a balance and no catalog record goes to an exceptions table for the catalog team to fix, outside analytical consumption.

Stockout traffic light

Order of evaluation; parameters in a single table.

Out of assortmentNo invoicing in 90 days, inactive or sales suspendedFORA_SORTIMENTO
No turnoverIn assortment, with a balance and no sales in 30 daysSEM_GIRO
StockoutZero available with active assortmentRUPTURA
CriticalCoverage below the replenishment lead timeCRITICO
AttentionCoverage below lead time plus the marginATENCAO
HealthyBetween the margin and target stockSAUDAVEL
ExcessCoverage above target stock in daysEXCESSO

Definitions

Available inventory
greater of (total minus reserved) and zero, by product and branch (or stock group), at the latest measurement, excluding unlimited and sentinel stock
In transit
quantity on its way to the location (incoming, inbound working + shipped + receiving), by product and location
Average daily sales
units from non-cancelled invoices that move inventory (outbound minus returns) over the last 30 days / 30, by product and branch
Days of coverage
(available + in transit) / average daily sales; null when there are no sales in 30 days
Stockout rate
active assortment products with zero available / active assortment products, by curve and day (closing measurement)
Days out of stock
closing measurements with zero available in the period, by product and branch
Estimated lost sales
average daily sales x days out of stock x average price, by product and month
Net requirement
open requisition minus (available + open purchase order), by product and ABC curve
Inventory value
quantity in stock x book cost, by product and branch

Pitfalls that already happened

No SKU matched

A query stripped leading zeros from the code on one side while the ERP inventory kept them. Practically no SKU matched inventory until the key became text with zeros.

Canonical code as text, padding by the ERP rule, never on marketplace SKUs.

Inventory lost through the slug

Extracting the product code from the friendly URL, instead of the reference field, left out about 80% of one country's inventory.

Code from the product reference field; slug only to extract attributes when there is no field.

Years of coverage

A marketplace returned a huge fixed quantity for unlimited stock listings and the channel's coverage shot up.

Sentinel flag with a validated threshold and exclusion from the sum.

Coverage underestimated at the channel warehouse

The calculation used only available stock. Once in-transit stock to the marketplace warehouse was included, that channel's coverage rose by about five days and some items left critical.

In transit in the coverage numerator, as the source BI did.

History that did not exist

The history was built as a SQL query and each run overwrote the previous one. There was no way to say how many days an item sat at zero.

A notebook that appends the measurement, with zero balance, dedup and retention.

Zeroed balance in the ERP

The quantity field in the product record came back as zero for many items with movements. The real balance was in the movements ledger.

Balance from the ledger's last movement, with forward fill on days without movement.

Acceptance test

The case is done when every item passes.

Agent package

Paste into Claude, ChatGPT or any client with the Nekt MCP connected.

Full Markdown
Inventory and coverage
- Current position: one row per product and branch. History: one row per product, branch, day and measurement (opening or closing, Brasília time).
- Available = greater of (total minus reserved) and zero. In transit is a separate column. Unlimited and sentinel stock stay out of the sum.
- Mirror branches aggregate by stock group, never summed.
- The product code is canonical text with the ERP padding rule. A marketplace SKU resolves by alias, direct code or warehouse suffix.
- Average daily sales = units from non-cancelled invoices that move inventory over the last 30 days / 30.
- Days of coverage = (available + in transit) / average daily sales. No sales in 30 days: no turnover, null coverage.
- Statuses: FORA_SORTIMENTO (out of assortment), SEM_GIRO (no turnover), RUPTURA (stockout), CRITICO (critical), ATENCAO (attention), SAUDAVEL (healthy), EXCESSO (excess). Parameters in the parameters table.
- ABC curve by cumulative 90-day revenue: A up to 80%, B up to 95%, C the rest.
- Stockouts over a period: use the history of closing measurements, not the current position.
- Say whether the product does not exist in the catalog or exists and is out of stock.
- State the date and time of the latest measurement in every availability answer.
Client exceptions:
- <client>: branches <A> and <B> in stock group <group>.
- <client>: target stock <days> days; attention margin <days> days; default replenishment lead time <days> days.
- <client>: sentinel threshold for channel <channel> = <quantity>.

Variations

Inventory in several countries

Position by country and product-color, joined with the country's sales on the same key, and a daily FX rate for valuation.

Multichannel seller

Inventory in each marketplace warehouse by base SKU, with target stock in days per channel.

Parts and after-sales

Parts inventory by company and supplier, with workshop requisitions and purchase orders in the net requirement.