# Item Availability _(Realtime-enabled)_

`GET` _(Table: EONE Item Availability)_

```
eOne/core/v1.0/itemInventoryAvailabilities
```

A read-only feed designed for **webhook subscriptions**, with one row per item + location (or a single row per item when you track stock company-wide). Each row carries on-hand, reserved, safety-stock and quantity-on-purchase-order figures, plus a pre-netted `availableQuantity`, and enough item detail to identify and route the product without a second call.

## Why it exists

This is a **maintained snapshot**, not a live calculation. The underlying table holds pre-computed quantities and is change-tracked, so instead of polling and recalculating availability yourself, you POST a subscription against this entity set; Business Central notifies you when a row changes, then you GET the row for the new quantities. Reading a row is a single indexed lookup. That is what makes the webhook cheap at scale.

## Off by default: enable it in Setup

The snapshot is **not maintained until you turn it on**. On a fresh install Item Availability is disabled and its refresh Job Queue entry is seeded in an *On Hold* state, so the extension adds no posting overhead until someone opts in. An admin enables it on the **eOne Integration Setup** page (search the page name in Business Central); enabling starts the refresh job and rebuilds the snapshot. Upgrades from a version that pre-dates this setting keep availability on, preserving the previous always-on behaviour.

While the feature is off, **this feed and the availability query return an error** rather than rows. It is the only read-only endpoint in the extension that does so. Nothing maintains the snapshot while it is off, so the rows it holds are the ones it had when it was switched off. An error tells you the feature is off; stale quantities would not.

## What you can configure

- **Track Inventory By.** Either *per location* (one row per item + location) or *company-wide* (a single aggregated row per item, with a **blank `locationCode`**). Choose company-wide if you do not track stock by location.
- **Available Quantity Formula.** How `availableQuantity` is netted: on-hand; on-hand less reserved; on-hand less reserved and safety stock; or projected (also adding inbound purchase orders). The raw components are always published, so you can still net your own way.
- **Include blocked items / in-transit locations / blank-location inventory.** Off by default; switch on to widen coverage.

> **Note:** Changing a setting re-evaluates the snapshot on the next job cycle: rows that no longer qualify are removed (sending subscribers a delete notification) and the rest are recomputed. Use the **Rebuild Availability Snapshot** action on the setup page to apply a change immediately rather than waiting for the cycle.

## How the snapshot stays current: a job queue drives it

Recompute does **not** happen inline when stock moves. It runs on a recurring **Job Queue** entry, so notifications are near-real-time but not instantaneous: they land on the job's cycle. The pipeline has three stages:

1. **Capture (instant).** Posting an inventory movement, changing a reservation, editing an outstanding purchase line, or changing item/SKU safety stock fires an event. The event does almost no work: it writes the affected `(item, location)` key into a small *buffer* table. A posting batch that touches the same key a thousand times collapses to one buffer row, so large postings stay fast. (When availability is disabled, even this capture step is skipped.)
2. **Drain (every few minutes).** The recurring Job Queue entry (seeded on install, activated when you enable availability, and running about every **5 minutes**) reads the buffered keys and recomputes each one once from the item's availability FlowFields, then clears it from the buffer. Keys queued by postings *during* a run are picked up on the next run. A key that fails to recompute is retried on later runs and only dropped after repeated failures, so one bad row never blocks the rest of the backlog.
3. **Notify (on real change only).** The recompute is idempotent and writes the row **only when a stored quantity actually changed**. Writing the row trips change tracking, which is what fires the webhook to subscribers. Re-running the job over an unchanged key writes nothing and raises no notification, so you never get spurious webhooks from a posting batch that did not move the figures.

> **Note:** Practical consequence: expect availability webhooks to arrive within a job-queue cycle of the posting (a few minutes), not the instant it posts. If notifications stop, check that availability is enabled and the recurring Job Queue entry is running (not On Hold). The in-product Item Availability Backlog page shows any keys stuck in the buffer.

## The row identifies its own item

A webhook tells you an item's stock moved. Acting on that usually means knowing *which* product it is and whether it can ship, which otherwise means a second call to `items` for every notification. So the row carries a small amount of item detail alongside the quantities:

| For | Fields |
| --- | --- |
| Identifying the product | `description`, `description2`, `gtin` |
| Sourcing | `vendorNumber`, `vendorItemNumber` |
| Fulfilment rules | `itemTrackingCode` |
| Customs | `tariffNumber` |

Two of these change how you should treat the row rather than merely describing it:

- **`itemTrackingCode`.** Non-blank means the item needs lot or serial numbers when it ships, so you know at notification time that fulfilment has to supply `itemTrackingLines`, rather than finding out when posting fails.
- **`gtin`.** The identifier most commerce platforms match on when they do not hold the Business Central item number, so it is usually the join key rather than `itemNo`.

Everything else about the item (pricing, category, weights, unit-of-measure conversions, the planning and posting setup) stays on the [items](https://eonesolutions.github.io/eone-bc-integration/core-items.md) endpoint. This feed answers "how much is there, and what is it", not "everything about it".

> **Note:** Every **quantity** on the row is in the item's *base* unit of measure. If you sell in a different unit, get the conversion factor from the item's `unitsOfMeasure` feed before showing sellable stock.

These fields are read live from the item, so they are always current. They are deliberately **not** part of what triggers a notification. Renaming an item does not fire an availability webhook; this stays a quantity feed, and the new value is there next time you read the row. Sync product data changes from the `items` feed.

## Coverage

Once enabled, the snapshot is seeded for every qualifying inventory item at every physical location (so brand-new items surface as out-of-stock rather than as a missing row), plus every item + location that has ledger history. What qualifies follows your settings: blocked items, in-transit locations, and blank-location movements are excluded unless you include them, and in company-wide mode there is a single row per item instead of one per location.

Filter by location with `locationIntegrationId` (your external key) as well as the Business Central `locationCode`. See [Resolver fields](https://eonesolutions.github.io/eone-bc-integration/resolvers.md).

> **Note:** Prefer a **pull** over a subscription? Two read-only queries expose the same stock-by-location data: `itemAvailabilitiesByLocationQuery` (reads this snapshot, which makes it the faster, preferred one) and the older `itemInventoryLevelQuantitiesByLocationQuery` (sums the ledger on each call). See [Reports / Queries](https://eonesolutions.github.io/eone-bc-integration/reports-queries.md).

## Item Availability fields

_API column → BC field._

**Legend**

- _(req)_ required on create
- _(res)_ resolver
- _(cur)_ currency-translated
- _(ro)_ read-only
- _(ext)_ added to this endpoint by another extension (BC field name shown; its API column name is not known here)

| API | BC field | Notes |
| --- | --- | --- |
| `id` | SystemId | BC record GUID. _(ro)_ |
| `bcCompanyId` | bcCompanyId | The company this row belongs to - the same GUID that appears in the request URL. Present on every endpoint so a store pooling several companies can keep them apart without relying on which URL it called. _(ro)_ |
| `bcCompanyName` | bcCompanyName | The name of the company this row belongs to. _(ro)_ |
| `itemNo` | Item No. | BC item number. _(ro)_ |
| `itemId` | Item Id | Item GUID. _(ro)_ |
| `ecommerceId` | Integration Id | Item external key. _(ro)_ |
| `inventoryId` | Inventory Id | Inventory external key. _(ro)_ |
| `blocked` | Blocked | Item blocked. _(ro)_ |
| `salesBlocked` | Sales Blocked | _(ro)_ |
| `baseUnitOfMeasure` | Base Unit of Measure | _(ro)_ |
| `description` | Description | Item description, so the availability row can be identified without a second call to the items endpoint. _(ro)_ |
| `description2` | Description 2 | Second description line. _(ro)_ |
| `gtin` | GTIN | Barcode / global trade item number - the identifier most commerce platforms match on when they do not hold the BC item number. _(ro)_ |
| `vendorNumber` | Vendor No. | Primary vendor, for routing a drop-ship or backorder. _(ro)_ |
| `vendorItemNumber` | Vendor Item No. | The vendor's own part number for this item. _(ro)_ |
| `itemTrackingCode` | Item Tracking Code | Non-blank means the item requires lot or serial numbers when it ships, so you know up front to supply itemTrackingLines. _(ro)_ |
| `tariffNumber` | Tariff No. | Tariff / commodity code, for customs paperwork. _(ro)_ |
| `locationCode` | Location Code | Per item + location; blank on the single row per item when tracking is set to company-wide. _(ro)_ |
| `locationIntegrationId` | Location Integration Id | Location external key. _(ro)_ |
| `onHandQuantity` | On-Hand Quantity | Quantity in stock. _(ro)_ |
| `reservedQuantity` | Reserved Quantity | _(ro)_ |
| `qtyOnPurchaseOrder` | Qty. on Purchase Order | Inbound on POs. _(ro)_ |
| `safetyStockQuantity` | Safety Stock Quantity | _(ro)_ |
| `availableQuantity` | Available Quantity | Pre-netted figure per the formula set in Setup; raw components above are still published. _(ro)_ |
| `lastModifiedDateTime` | SystemModifiedAt | Changes here fire the webhook. _(ro)_ |
