stevebauman/inventory
A Laravel package for inventory management. Track items, categories, suppliers, and locations, monitor stock levels and inventory movements, and manage receiving and issuing through Eloquent models and migrations. Quickly add reliable inventory features to any Laravel application.
Reality check first. This package was last released in June 2015 and targets Laravel 4 / 5. Before writing a line of code, confirm your app can actually run it: php constraints, illuminate/support version, and whether you need composer require stevebauman/inventory at all versus forking it into packages/. On a modern Laravel app (9/10/11) you will almost certainly need to fork and patch the service provider, config, and migrations. Treat it as a starting schema and domain model rather than a drop-in dependency.
Minimal setup on a compatible Laravel 5.x app:
composer require stevebauman/inventoryStevebauman\Inventory\InventoryServiceProvider::class to config/app.php under providers.php artisan vendor:publish (config file and migrations, depending on the package version). Check config/inventory.php for model bindings before running anything.php artisan migrate — this creates tables for items, categories, suppliers, locations, and stock movements.null but leave you with orphaned records you'll regret later.Where to look first: the published config (config/inventory.php) and the item model it points to. The config usually lets you swap the item class and the table names, which is the main extension point you'll use. Then read the item model's trait — stock methods (addStock, removeStock, getStock, getTotalCost, getAverageCost, isDepleted, hasStock) and its relationships (category, supplier, location, movements) are the real API surface.
First use case: a single warehouse, one supplier, manual stock adjustments. Create an item with a SKU, category, supplier, and location; call addStock($qty, $reason, $cost); verify a movement row was written and getStock() returns the expected quantity. That vertical slice exercises the migrations, the trait, the movement audit trail, and the cost calculation in one go. Do it in a test before trusting it in production.
Wrap every stock mutation in a transaction and a service class. The package mutates the item row and writes a movement row. If the second write fails, your quantity and your audit trail diverge silently. Create an App\Services\StockService with methods like receive(), issue(), adjust(), transfer() that open a DB::transaction, lock the item row (lockForUpdate()), call the package method, and dispatch your own domain event. This is the single highest-leverage thing you can do — it isolates the package behind a seam you control.
Treat movements as the source of truth, quantity as a cache. The package keeps a quantity column on the item and appends movements. Reconcile regularly: a scheduled command that sums movements per item and compares to getStock(), logging or auto-correcting drift. This catches bugs, manual DB edits, and race conditions before finance does.
Build a thin repository around item lookup. Day-to-day code will constantly ask "find this SKU," "items below reorder point," "items at location X." The package's query surface is plain Eloquent, so add local scopes on your own subclass (or a repository) rather than scattering where('quantity', '<', ...) everywhere. Subclassing the item model and pointing config/inventory.php at your subclass is the intended customization path — use it for accessors, scopes, and appended attributes.
Use the events for notifications, not for correctness. If the package fires stock-added / stock-removed / depleted events, hook listeners for low-stock alerts and Slack/email. Do not put ledger writes or downstream state changes in those listeners — they may not be transactionally aligned with the mutation. Anything that must be consistent belongs inside your service's transaction.
Model categories and suppliers as first-class, not as package concerns. In practice teams outgrow the bundled category/supplier/location models quickly (hierarchies, multiple suppliers per item, bin-level locations). Keep them for MVP, but design your service and API around your own vocabulary so swapping in richer models later is a config + migration change, not a rewrite.
For anything multi-location, plan a transfer primitive. If the package exposes a move/transfer method, verify it writes two movements (out from source, in to destination) and is atomic. If it doesn't, implement transfer in your service as removeStock + addStock inside one transaction, with a shared reference ID so the pair is traceable.
Testing pattern: use the package migrations in RefreshDatabase, build factories for item/category/supplier/location, and assert on both item quantity and movement count after each call. Assert on movement reason strings too — they're your only free-form audit metadata and teams rely on them heavily in reporting.
repositories in composer.json or a packages/ path repo), pin it, and own the maintenance. Budget a day for the fork-and-patch if you're on a modern stack.vendor:publish.migrate without publishing gives you missing tables with no error. Publish, inspect the migration files, then migrate — and check for column type choices you disagree with.removeStock calls can both read quantity 10, both write 8. Always lockForUpdate() the item inside your service transaction; never call the package methods directly from controllers.created_at, sum quantity deltas, diff against getStock(). If they disagree, look for direct $item->update(['quantity' => ...]) calls — grep your codebase for writes to the quantity column and ban them.How can I help you explore Laravel packages today?