karnoweb / laravel-inventory
Warehouse inventory package for Laravel: batches, FIFO/FEFO, transfers, reservations, and stock counts.
Requires
- php: ^8.2
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/events: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.5|^11.0|^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A Laravel inventory ledger for receiving, issuing, reserving, transferring, counting, and valuing stock. It tracks quantities through immutable movements instead of a mutable stock column.
Basic usage needs only a stockable Laravel model, a warehouse, a quantity, and a unit cost. Branches, locations, serials, units of measure, recipes, expiry, and costing choices are optional.
Requirements
- PHP
^8.2 - Laravel
11,12, or13
Installation
composer require karnoweb/laravel-inventory:^13.3 php artisan migrate
The package migrations are auto-discovered. Publish configuration only when changing defaults:
php artisan vendor:publish --tag=inventory-config
Five-minute quick start
Make the model whose stock is tracked inventory-aware:
use Illuminate\Database\Eloquent\Model; use Karnoweb\Inventory\Traits\HasInventory; class Product extends Model { use HasInventory; }
Use one product and one warehouse. branch_id is optional.
use Karnoweb\Inventory\Facades\Inventory; $chips = Product::query()->firstOrFail(); $warehouse = Inventory::warehouses()->create([ 'name' => 'Main Warehouse', 'branch_id' => null, ]); Inventory::stock()->receive($chips, 10, 10_000, warehouseId: $warehouse->id); Inventory::stock()->receive($chips, 10, 20_000, warehouseId: $warehouse->id); Inventory::stock()->onHand($chips, $warehouse->id); // 20 physical units Inventory::reservations()->reserve($chips, 5, warehouseId: $warehouse->id); Inventory::stock()->available($chips, $warehouse->id); // 15 units still issuable Inventory::stock()->issue($chips, 3, warehouseId: $warehouse->id);
onHand is physical stock. reserved is promised stock. available is sellable stock after active reservations and expired batches are excluded.
20 on hand
- 5 reserved
= 15 available
Use a source model when an operation belongs to a business record. Usually use an item, such as OrderItem, rather than a whole Order.
Inventory::stock()->issue($chips, 3, warehouseId: $warehouse->id, sourceable: $orderItem); Inventory::stock()->returnIssue($returnItem, $orderItem, 1);
Create and confirm a transfer separately. Creating it does not move stock.
$transfer = Inventory::transfers()->create($warehouse, $otherWarehouse, [[ 'stockable' => $chips, 'quantity' => 3, ]]); Inventory::transfers()->confirm($transfer);
Optional features
- FIFO, FEFO, WAC: configure
inventory.general.costing. - Locations: enable
inventory.location.enabledto receive or issue from a shelf. - Serials: enable
inventory.serial.enabled, then settracks_serialfor a product. - UOM: enable
inventory.uom.enabledfor integer base-unit conversions. - Recipes: enable
inventory.recipe.enabledonly for composed products. - Branches: provide a host-owned
inventory.branch.resolverwhen tenant isolation is needed.
Documentation
Start with Getting started, then read the mental model. The full learning reference is in docs.
Testing
vendor/bin/phpunit