tomatophp/tomato-pos
Tomato POS is a Laravel package for adding point-of-sale workflows to your app, with modules for products, orders, payments, and receipts. It fits the TomatoPHP ecosystem for building retail or restaurant POS features quickly.
Tomato POS is a Filament-based point-of-sale module for the TomatoPHP admin suite. It is not a standalone package — it expects the TomatoPHP panel (or at minimum a Filament 3 panel) plus the sibling commerce packages that supply products, customers, and orders.
tomatophp/tomato-admin (or a Filament 3 panel), plus the product/category/customer packages the POS screen reads from. Installing tomatophp/tomato-pos alone gives you an empty till.composer require tomatophp/tomato-pos.php artisan migrate — check the migration file list before running on an existing database (see Gotchas regarding table name collisions).->plugin(\TomatoPHP\TomatoPos\TomatoPosPlugin::make()) or the Filament 3 TomatoPos::make() form, depending on the version you resolve). On a TomatoPHP panel this is usually auto-discovered via the plugins config — verify before adding it twice.php artisan vendor:publish and inspect the available tags for tomato-pos before assuming tag names.Where to look first: config/ (currency, tax, receipt, table prefix), then src/ for the Filament page/resource classes that render the POS screen, then resources/views for the receipt template. Read the migrations early — they tell you more about the data model than the README does.
First use case: ring up a walk-in sale end to end — open the POS page, add two products to the cart, apply a discount, take a cash payment, and print/preview the receipt. That single path exercises the cart state, order creation, payment recording, and the receipt view, and it will surface 90% of configuration problems in an afternoon.
Treat it as a module in a suite, not an app. The POS screen is a thin UI over models owned by other TomatoPHP packages. Your integration work happens at the boundaries: product availability, customer lookup, and order finalisation. Wire your business rules there rather than forking the POS page.
Extending the Filament resources. The supported pattern is subclassing — extend the package's resource/page classes, override form(), table(), getPages(), or the cart action handlers, and register your subclass in the panel instead of the original. This survives patch upgrades far better than editing published views.
Swapping models via config. If the package exposes model bindings in its config (common in the TomatoPHP family), prefer overriding the model class over editing vendor code. Keeps composer update safe.
Hooking order lifecycle. Register observers or model events on the order/order-item models for the things a POS must do beyond the UI: decrement stock, write an audit row, push to accounting, fire a receipt print job. Do this in a service provider rather than inside the POS page so refunds and admin-created orders flow through the same logic.
Localisation (new in v1.0.14). Ship your own locale by publishing the language files and placing overrides in lang/vendor/tomato-pos/{locale}/. Set the panel/locale per user so cashiers get their language without changing the app default. If you serve RTL locales, the v1.0.14 style updates are worth re-testing specifically — cart and receipt layouts are the usual breakage points.
Receipts. Publish the receipt Blade view into your app and own it there. Pass a plain array/DTO into the view rather than the Eloquent model so you can reuse it for reprints, email, and 80mm thermal output without dragging relations along.
Multi-terminal / multi-register. If you run several tills, key the POS session by terminal/register ID and keep cart state per terminal. Livewire cart state in the browser is fine for one till; two cashiers sharing a session is a data-integrity bug waiting to happen.
Testing. Write Livewire tests against the POS page component: add item, change quantity, apply tax, submit. These are cheap and catch the regressions that patch releases in a UI-heavy package tend to introduce.
orders, order_items, payments, accounts-style tables. On a brownfield app this collides. Check the migrations before php artisan migrate and look for a table-prefix config key; if there isn't one, you're renaming migrations and updating the models — do it deliberately, once.php artisan optimize:clear plus php artisan filament:assets is the fix for a surprising share of "the plugin doesn't appear" reports. Filament's component/panel caching is aggressive.float, do your totals in integer minor units in your own layer and let the package display them. Tax rounding on multi-line carts is where this bites.filament-shield/Spatie permissions. Configure roles early; a cashier who can reach the product-edit screen is a liability.storage/logs/laravel.log covers most issues. If actions silently do nothing, it's usually a swallowed validation error or a policy denial — check canViewAny/canCreate on the resource before assuming a bug.composer.json ("tomatophp/tomato-pos": "~1.0.14") if you're in production. Patch releases here are UI and translation churn, which is exactly the category most likely to move markup you've styled around.How can I help you explore Laravel packages today?