Add POS Pro subscription billing and free-tier limits.
Deploy Ladill POS / deploy (push) Successful in 43s

Wallet-backed Pro unlocks unlimited products, restaurant mode, catalog imports, and ecosystem sync features.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-30 00:51:35 +00:00
co-authored by Cursor
parent 68255786e1
commit f800f0c1ca
17 changed files with 635 additions and 29 deletions
+3
View File
@@ -2,7 +2,10 @@
use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schedule;
Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote');
Schedule::command('pos:pro-renew')->dailyAt('02:50')->withoutOverlapping();
+31 -26
View File
@@ -5,7 +5,7 @@ use App\Http\Controllers\Pos\AiController;
use App\Http\Controllers\Pos\DashboardController;
use App\Http\Controllers\Pos\KitchenController;
use App\Http\Controllers\Pos\MenuController;
use App\Http\Controllers\Pos\ProductController;
use App\Http\Controllers\Pos\ProController;
use App\Http\Controllers\Pos\RegisterController;
use App\Http\Controllers\Pos\SaleController;
use App\Http\Controllers\Pos\SettingsController;
@@ -59,31 +59,32 @@ Route::middleware(['auth', 'platform.session'])->group(function () {
Route::delete('/sales/{sale}', [SaleController::class, 'destroy'])->name('pos.sales.destroy');
// Restaurant mode — floor, open tickets (tabs), and the kitchen display.
Route::get('/floor', [TableController::class, 'index'])->name('pos.floor');
Route::get('/floor/tables/{table}/qr', [TableController::class, 'qr'])->name('pos.tables.qr');
Route::post('/tickets', [TicketController::class, 'open'])->name('pos.tickets.open');
Route::get('/tickets/{sale}', [TicketController::class, 'show'])->name('pos.tickets.show');
Route::post('/tickets/{sale}/lines', [TicketController::class, 'addLine'])->name('pos.tickets.lines.add');
Route::patch('/tickets/{sale}/lines/{line}', [TicketController::class, 'updateLine'])->name('pos.tickets.lines.update');
Route::delete('/tickets/{sale}/lines/{line}', [TicketController::class, 'removeLine'])->name('pos.tickets.lines.remove');
Route::post('/tickets/{sale}/send', [TicketController::class, 'sendToKitchen'])->name('pos.tickets.send');
Route::post('/tickets/{sale}/settle', [TicketController::class, 'settle'])->name('pos.tickets.settle');
Route::middleware('pro')->group(function () {
Route::get('/floor', [TableController::class, 'index'])->name('pos.floor');
Route::get('/floor/tables/{table}/qr', [TableController::class, 'qr'])->name('pos.tables.qr');
Route::post('/tickets', [TicketController::class, 'open'])->name('pos.tickets.open');
Route::get('/tickets/{sale}', [TicketController::class, 'show'])->name('pos.tickets.show');
Route::post('/tickets/{sale}/lines', [TicketController::class, 'addLine'])->name('pos.tickets.lines.add');
Route::patch('/tickets/{sale}/lines/{line}', [TicketController::class, 'updateLine'])->name('pos.tickets.lines.update');
Route::delete('/tickets/{sale}/lines/{line}', [TicketController::class, 'removeLine'])->name('pos.tickets.lines.remove');
Route::post('/tickets/{sale}/send', [TicketController::class, 'sendToKitchen'])->name('pos.tickets.send');
Route::post('/tickets/{sale}/settle', [TicketController::class, 'settle'])->name('pos.tickets.settle');
Route::get('/kitchen', [KitchenController::class, 'index'])->name('pos.kitchen');
Route::get('/kitchen/feed', [KitchenController::class, 'feed'])->name('pos.kitchen.feed');
Route::get('/kitchen/stream', [KitchenController::class, 'stream'])->name('pos.kitchen.stream');
Route::post('/kitchen/lines/{line}/bump', [KitchenController::class, 'bump'])->name('pos.kitchen.bump');
Route::get('/kitchen', [KitchenController::class, 'index'])->name('pos.kitchen');
Route::get('/kitchen/feed', [KitchenController::class, 'feed'])->name('pos.kitchen.feed');
Route::get('/kitchen/stream', [KitchenController::class, 'stream'])->name('pos.kitchen.stream');
Route::post('/kitchen/lines/{line}/bump', [KitchenController::class, 'bump'])->name('pos.kitchen.bump');
// Menu depth — categories, kitchen stations, and modifier groups/options.
Route::get('/menu', [MenuController::class, 'index'])->name('pos.menu');
Route::post('/menu/categories', [MenuController::class, 'storeCategory'])->name('pos.menu.categories.store');
Route::delete('/menu/categories/{category}', [MenuController::class, 'destroyCategory'])->name('pos.menu.categories.destroy');
Route::post('/menu/stations', [MenuController::class, 'storeStation'])->name('pos.menu.stations.store');
Route::delete('/menu/stations/{station}', [MenuController::class, 'destroyStation'])->name('pos.menu.stations.destroy');
Route::post('/menu/groups', [MenuController::class, 'storeGroup'])->name('pos.menu.groups.store');
Route::delete('/menu/groups/{group}', [MenuController::class, 'destroyGroup'])->name('pos.menu.groups.destroy');
Route::post('/menu/groups/{group}/modifiers', [MenuController::class, 'storeModifier'])->name('pos.menu.modifiers.store');
Route::delete('/menu/modifiers/{modifier}', [MenuController::class, 'destroyModifier'])->name('pos.menu.modifiers.destroy');
Route::get('/menu', [MenuController::class, 'index'])->name('pos.menu');
Route::post('/menu/categories', [MenuController::class, 'storeCategory'])->name('pos.menu.categories.store');
Route::delete('/menu/categories/{category}', [MenuController::class, 'destroyCategory'])->name('pos.menu.categories.destroy');
Route::post('/menu/stations', [MenuController::class, 'storeStation'])->name('pos.menu.stations.store');
Route::delete('/menu/stations/{station}', [MenuController::class, 'destroyStation'])->name('pos.menu.stations.destroy');
Route::post('/menu/groups', [MenuController::class, 'storeGroup'])->name('pos.menu.groups.store');
Route::delete('/menu/groups/{group}', [MenuController::class, 'destroyGroup'])->name('pos.menu.groups.destroy');
Route::post('/menu/groups/{group}/modifiers', [MenuController::class, 'storeModifier'])->name('pos.menu.modifiers.store');
Route::delete('/menu/modifiers/{modifier}', [MenuController::class, 'destroyModifier'])->name('pos.menu.modifiers.destroy');
});
Route::get('/products', [ProductController::class, 'index'])->name('pos.products.index');
Route::get('/products/import', [ProductController::class, 'importForm'])->name('pos.products.import.form');
@@ -97,10 +98,14 @@ Route::middleware(['auth', 'platform.session'])->group(function () {
Route::get('/settings', [SettingsController::class, 'index'])->name('pos.settings');
Route::put('/settings', [SettingsController::class, 'update'])->name('pos.settings.update');
Route::post('/settings/import-crm', [SettingsController::class, 'importCrm'])->name('pos.settings.import-crm');
Route::post('/settings/import-merchant', [SettingsController::class, 'importMerchant'])->name('pos.settings.import-merchant');
Route::post('/settings/import-crm', [SettingsController::class, 'importCrm'])->middleware('pro')->name('pos.settings.import-crm');
Route::post('/settings/import-merchant', [SettingsController::class, 'importMerchant'])->middleware('pro')->name('pos.settings.import-merchant');
Route::post('/settings/tables', [SettingsController::class, 'storeTable'])->name('pos.settings.tables.store');
Route::delete('/settings/tables/{table}', [SettingsController::class, 'destroyTable'])->name('pos.settings.tables.destroy');
Route::get('/wallet', fn () => redirect()->away(ladill_account_url('/wallet')))->name('pos.wallet');
Route::get('/pro', [ProController::class, 'index'])->name('pos.pro.index');
Route::post('/pro/subscribe', [ProController::class, 'subscribe'])->name('pos.pro.subscribe');
Route::post('/pro/cancel', [ProController::class, 'cancel'])->name('pos.pro.cancel');
});