Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tomato Inventory Module Laravel Package

tomatophp/tomato-inventory-module

TomatoPHP Inventory module for Laravel: track products, stock levels, warehouses, and movements from your admin panel. Ships with CRUD resources, stock adjustment tools, low-stock alerts, and reporting hooks so you can wire inventory into any TomatoPHP app.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimum viable setup (30–60 min)

composer require tomatophp/tomato-inventory-module:1.0.0   # pin exactly, not ^1.0
php artisan vendor:publish --tag=tomato-inventory-config   # verify tag name in README
php artisan vendor:publish --tag=tomato-inventory-migrations
php artisan migrate
php artisan optimize:clear

Then register the module with the TomatoPHP module loader (php artisan module:install / the module manager UI, depending on your stack version) and confirm the Filament resources appear in the panel navigation. If nothing shows up, the problem is almost always module registration or panel provider discovery, not the module itself.

Where to look first, in this order

  1. composer.json — the php, laravel/framework, and filament/filament constraints. This single file decides whether you can adopt the package at all. Filament v3 vs v4 is a hard gate.
  2. database/migrations/ — read these before reading any PHP. The schema tells you whether the module owns a real stock-movement ledger or just mutates a quantity column on a products table. That distinction determines ~80% of how you'll build on top of it.
  3. src/Models/ — which models are the module's, and which (Product?) might collide with or reference your existing catalog.
  4. src/Filament/Resources/ — the actual day-to-day surface your ops users will touch.
  5. config/ — publish it and read it; table names, feature toggles, and navigation settings usually live here.

First use case to build

Pick the narrowest thing that exercises the whole data path: one warehouse, receive stock for a product, adjust it, see the low-stock alert fire. If you can do that in a sandbox in half a day, you've answered the questions that matter (does it own a ledger, are adjustments auditable, does the report layer work). If the schema fights you, that is a strong negative signal before you've written any integration code.

Do this in a throwaway app with your real-shaped product data, not with three seeded rows. Volume and SKU weirdness is where these modules break.

Implementation Patterns

Treat the module as the ledger owner — never write quantity columns directly

The single most important discipline: your application code should create movements, not mutate stock levels. If the module exposes a movement/adjustment action, route everything through it. If it doesn't, wrap whatever it does expose behind your own service so there's exactly one write path.

// Good: one chokepoint, auditable, reversible
final class StockService
{
    public function receive(int $productId, int $warehouseId, int $qty, string $ref): void
    {
        // module's movement/adjustment API (verify exact class in src/)
        InventoryMovement::create([...]);
    }
}

Every direct $product->update(['quantity' => ...]) you allow is a future stock-drift bug.

Opening balances as movements, not as seeded quantities

When backfilling your existing catalog, generate a synthetic "opening balance" movement per SKU/warehouse rather than setting the quantity field. You get an audit trail from day one, and you can reconcile the ledger sum against the stored level to prove the module's math agrees with itself.

foreach ($openingStock as $row) {
    $stock->receive($row->product_id, $row->warehouse_id, $row->qty, 'OPENING-BALANCE-2024');
}

Isolate the module behind your own boundary

Because v1.0.0 is a first tag with a single maintainer, don't let module models leak into controllers, jobs, and API resources. Define a thin adapter layer (InventoryGateway interface + module-backed implementation) so that a fork, a patch, or a replacement is contained. This costs an afternoon and saves a rewrite.

Extending the Filament surface

Follow the ecosystem convention rather than fighting it: extend the module's resources/pages rather than registering parallel ones, add your own widgets to the inventory panel, and use the module's policies as the base for your permission layer. If TomatoPHP roles/permissions are installed, wire inventory permissions there so warehouse staff and admins get different visibility without forking the resources.

// Typical pattern: subclass and re-register in your panel provider
class ProductResource extends \TomatoPHP\Inventory\Filament\Resources\ProductResource
{
    public static function getNavigationGroup(): ?string { return 'Warehouse'; }
}

Reporting and barcode print

Low-stock reports and barcode printing are advertised features; treat both as "verify before promising to stakeholders." Barcode output almost certainly keys off SKU, so make sure your SKU mapping is correct and stable before anyone prints labels onto physical shelves — reprinting a warehouse is a genuinely expensive mistake. For anything the module's reports don't cover, query the movement table directly with aggregation rather than recomputing from product rows.

Rollout sequencing

Feature-flag the whole module. Go read-only (or single-warehouse) first, keep the legacy source authoritative, and reconcile nightly: sum(movements) == stock_level for every SKU. Only flip to authoritative once parity holds for a week. Multi-warehouse transfers are the highest-risk flow — test those explicitly and last.

Gotchas and Tips

v1.0.0 means "first tag," not "stable API." The released changelog is an empty compare link. Pin the exact version in composer.json, commit the lockfile, and read the git diff between tags before every upgrade. Do not float on ^1.0 until a 1.0.x patch history exists that demonstrates discipline.

Filament major version is a trap door. You will likely be pinned to whatever Filament version the module supports. A routine composer update on the panel can break every inventory resource silently. Add the module to your upgrade calendar as a blocked-on-upstream item and test panel upgrades against a branch.

Migrations may not be reversible. Check for down() before your first production migrate — a schema you can't roll back on a table holding stock history is a bad afternoon. Take a database backup immediately before migrating, every time.

Concurrency is unverified and it matters. Two users adjusting the same stock line is the canonical failure case. Test it deliberately: hammer one SKU with parallel adjustments and confirm you get atomic decrements and correct totals, not lost updates. If the module doesn't lock rows, add lockForUpdate() in your service wrapper around its writes — that's a one-line fix if you built the chokepoint described above.

Multi-warehouse transfers double-count. Classic bug in this category of package: stock appears in both warehouses, or vanishes. Reconcile nightly during beta and treat any drift as a blocker, not a rounding error.

Tenant scoping is unconfirmed. If you're multi-tenant, verify explicitly that inventory tables carry a tenant key or can be scoped without patching the module. Retrofitting tenancy onto a global-scope model is painful.

Costing (FIFO / weighted average) and lot/serial tracking are likely out of scope. Confirm before designing anything that depends on them. If you need either, budget for owning a fork — that materially changes the buy-vs-build calculation.

Bus factor is one. Set up a fork procedure before you need it: a mirror in your org, a documented patch process, and a named internal owner. Support is GitHub issues only; there's no vendor to escalate to.

Debugging tips that actually help:

  • DB::listen() or Laravel Telescope around any inventory write — confirm exactly what rows the module touches per action.
  • Build a throwaway "movement audit" Filament widget early; you will use it constantly during reconciliation.
  • When numbers look wrong, recompute from the movement table first. If the ledger is right and the level is wrong, you have a sync bug. If both are wrong, you have a write-path bug. That distinction halves your debugging time.

Extension points to check before forking anything: published config, published migrations/views, resource subclassing, model binding in the service container, and Filament panel-provider registration. Most TomatoPHP modules are designed to be overridden this way — reach for those before you vendor the package.

Ramp-up expectation: a Filament-fluent Laravel developer needs about a day to read the module and run the smoke test. The learning curve isn't the code, it's the domain concept — movements vs. levels — and internalizing that is what prevents the bugs listed above.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin