# @medusajs/inventory

Inventory Module (`Modules.INVENTORY`): inventory items, their stock per location (inventory levels), and reservations against that stock.

## Using this package

- Registered by default; consumers import it as `@medusajs/medusa/inventory`. No module options.
- Resolve: `container.resolve(Modules.INVENTORY)` (type `IInventoryService` from `@medusajs/framework/types`). Prefer `@medusajs/core-flows` workflows (e.g. reservation and inventory-level workflows) for mutations.

**Data models** (`src/models`):

- `InventoryItem` (`iitem_`): `sku` (unique among non-deleted), customs/dimension fields, `requires_shipping`. Computed `stocked_quantity` / `reserved_quantity` are lazy SQL formulas summing its levels (`src/utils/apply-decorators.ts`). Deleting cascades to `location_levels` and `reservation_items`.
- `InventoryLevel` (`ilev_`): one per `(inventory_item_id, location_id)` (unique), `stocked_quantity`, `reserved_quantity`, `incoming_quantity`; `available_quantity` = stocked - reserved, computed in an `onInit` hook.
- `ReservationItem` (`resitem_`): `inventory_item_id`, `location_id`, `quantity`, `line_item_id`, `allow_backorder`, `external_id`.
- `location_id` is a plain string pointing at the Stock Location Module; there is no FK.

**Custom service methods** (`src/services/inventory-module.ts`):

- `adjustInventory(itemId, locationId, adjustment)` or `adjustInventory([{ inventoryItemId, locationId, adjustment }])`: adds `adjustment` to `stocked_quantity`.
- `confirmInventory(itemId, locationIds, quantity)`, `retrieveAvailableQuantity`, `retrieveStockedQuantity`, `retrieveReservedQuantity` (summed over `locationIds`; empty array returns 0), `retrieveInventoryLevelByItemAndLocation`.
- `deleteInventoryLevel(itemId, locationId)`, `deleteInventoryItemLevelByLocationId(locationIds)` (soft delete).
- `deleteReservationItemByLocationId`, `deleteReservationItemsByLineItem`, `restoreReservationItemsByLineItem`.
- `create/update/delete/softDelete/restoreReservationItems`, `create/updateInventoryLevels`, `create/updateInventoryItems` are overridden to keep `reserved_quantity` in sync; the rest is generated by `MedusaService`.

**Events**: `InventoryEvents` from `@medusajs/framework/utils` (`inventory.inventory-item.*`, `inventory.inventory-level.*`, `inventory.reservation-item.*`), emitted from ORM mutation hooks by `MedusaService` in `@EmitEvents` methods.

**Module links** (in `packages/modules/link-modules`):

- `product_variant_inventory_item` (`ProductVariant` <-> `InventoryItem`, extra column `required_quantity`, default 1; exposes `variant.inventory_items` and `inventory_item.variants`).
- Read-only: `InventoryLevel.location_id` -> stock location (`stock_locations`).

Docs: https://docs.medusajs.com/resources/commerce-modules/inventory.md, https://docs.medusajs.com/resources/commerce-modules/inventory/concepts.md, https://docs.medusajs.com/resources/commerce-modules/inventory/reservations-lifecycle.md, https://docs.medusajs.com/resources/commerce-modules/inventory/inventory-kit.md

## Architecture

- `src/services/inventory-level.ts` (`InventoryLevelService`) wraps `src/repositories/inventory-level.ts`, whose `getAvailableQuantity` / `getStockedQuantity` / `getReservedQuantity` aggregate in SQL. Importing it calls `applyEntityHooks()`, which patches the MikroORM entities with the `onInit` hook and `@Formula` columns.
- `src/joiner-config.ts`: aliases `inventory_items` / `inventory_item` / `inventory` -> `InventoryItem`, `reservation(s)` / `reservation_item(s)` -> `ReservationItem`.

## Development

```bash
cd packages/modules/inventory
yarn build
yarn test
yarn test:integration          # needs Postgres
../../../node_modules/.bin/jest integration-tests/__tests__/inventory-module-service.spec.ts -t "adjustInventory"
yarn migration:create          # after any change in src/models
```

- Integration tests: `integration-tests/__tests__/inventory-module-service.spec.ts`.

## Gotchas

- `reserved_quantity` cannot be set through `createInventoryLevels` / `updateInventoryLevels`; it is stripped from input. It only moves when reservations are created, updated (quantity or location change), deleted, soft-deleted or restored.
- `location_levels` is stripped from `createInventoryItems` / `updateInventoryItems` input; create levels separately.
- `createReservationItems` throws `NOT_FOUND` if the item is not stocked at the location and `NOT_ALLOWED` ("Not enough stock available") when `available_quantity < quantity`, unless `allow_backorder` is true.
- `updateInventoryLevels` addresses levels by `inventory_item_id` + `location_id`, not by `id`.
- `adjustInventory` with an array runs each adjustment sequentially in its own transaction.
- Quantities are `bigNumber` columns; use `MathBN` / `BigNumber` from `@medusajs/framework/utils`, not JS arithmetic.
