Add Frontdesk-style Devices registry for POS hardware.
Deploy Ladill POS / deploy (push) Successful in 42s
Deploy Ladill POS / deploy (push) Successful in 42s
Register tills, customer displays, kitchen screens, printers, scanners, and tablets per branch. Customer displays share the branch display token and mark online when the public screen polls; stale devices go offline on a schedule.
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Pos;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Pos\Concerns\ScopesToAccount;
|
||||
use App\Models\PosDevice;
|
||||
use App\Models\PosLocation;
|
||||
use App\Services\Pos\DeviceService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class DeviceController extends Controller
|
||||
{
|
||||
use ScopesToAccount;
|
||||
|
||||
public function __construct(private DeviceService $devices) {}
|
||||
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$owner = $this->ownerRef($request);
|
||||
|
||||
$query = PosDevice::owned($owner)->with('location')->orderBy('name');
|
||||
$this->scopeToLocation($request, $query);
|
||||
|
||||
$devices = $query->paginate(25);
|
||||
|
||||
$statsQuery = PosDevice::owned($owner);
|
||||
$this->scopeToLocation($request, $statsQuery);
|
||||
|
||||
$heroStats = [
|
||||
'total' => (clone $statsQuery)->count(),
|
||||
'online' => (clone $statsQuery)
|
||||
->where('status', PosDevice::STATUS_ONLINE)
|
||||
->where('last_online_at', '>=', now()->subMinutes(10))
|
||||
->count(),
|
||||
'displays' => (clone $statsQuery)
|
||||
->whereIn('type', ['customer_display', 'kitchen_display'])
|
||||
->count(),
|
||||
];
|
||||
|
||||
return view('pos.devices.index', [
|
||||
'devices' => $devices,
|
||||
'deviceTypes' => config('pos.device_types', []),
|
||||
'heroStats' => $heroStats,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(Request $request): View
|
||||
{
|
||||
return view('pos.devices.create', [
|
||||
'deviceTypes' => config('pos.device_types', []),
|
||||
'hints' => config('pos.device_registration_hints', []),
|
||||
'locations' => $this->locations($request),
|
||||
'defaultLocationId' => $this->location($request)->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$types = array_keys(config('pos.device_types', []));
|
||||
$owner = $this->ownerRef($request);
|
||||
|
||||
$validated = $request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'type' => ['required', 'string', Rule::in($types)],
|
||||
'location_id' => ['nullable', 'integer'],
|
||||
]);
|
||||
|
||||
$locationId = $validated['location_id'] ?? null;
|
||||
if ($locationId) {
|
||||
$this->assertLocationOwned($request, (int) $locationId);
|
||||
}
|
||||
|
||||
if (in_array($validated['type'], ['customer_display', 'kitchen_display'], true) && ! $locationId) {
|
||||
return back()->withInput()->with('error', 'Choose a branch for this display device.');
|
||||
}
|
||||
|
||||
$this->devices->register([
|
||||
'owner_ref' => $owner,
|
||||
'name' => $validated['name'],
|
||||
'type' => $validated['type'],
|
||||
'location_id' => $locationId,
|
||||
]);
|
||||
|
||||
return redirect()->route('pos.devices.index')->with('success', 'Device registered.');
|
||||
}
|
||||
|
||||
public function edit(Request $request, PosDevice $device): View
|
||||
{
|
||||
$this->authorizeOwner($request, $device);
|
||||
|
||||
return view('pos.devices.edit', [
|
||||
'device' => $device,
|
||||
'deviceTypes' => config('pos.device_types', []),
|
||||
'hints' => config('pos.device_registration_hints', []),
|
||||
'locations' => $this->locations($request),
|
||||
'openUrl' => $this->devices->openUrl($device),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, PosDevice $device): RedirectResponse
|
||||
{
|
||||
$this->authorizeOwner($request, $device);
|
||||
|
||||
$types = array_keys(config('pos.device_types', []));
|
||||
$validated = $request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'type' => ['required', 'string', Rule::in($types)],
|
||||
'location_id' => ['nullable', 'integer'],
|
||||
'status' => ['nullable', 'string', Rule::in([
|
||||
PosDevice::STATUS_ONLINE,
|
||||
PosDevice::STATUS_OFFLINE,
|
||||
PosDevice::STATUS_MAINTENANCE,
|
||||
])],
|
||||
]);
|
||||
|
||||
if (! empty($validated['location_id'])) {
|
||||
$this->assertLocationOwned($request, (int) $validated['location_id']);
|
||||
}
|
||||
|
||||
$device->update([
|
||||
'name' => $validated['name'],
|
||||
'type' => $validated['type'],
|
||||
'location_id' => $validated['location_id'] ?: null,
|
||||
'status' => $validated['status'] ?? $device->status,
|
||||
]);
|
||||
|
||||
return redirect()->route('pos.devices.index')->with('success', 'Device updated.');
|
||||
}
|
||||
|
||||
public function destroy(Request $request, PosDevice $device): RedirectResponse
|
||||
{
|
||||
$this->authorizeOwner($request, $device);
|
||||
$device->delete();
|
||||
|
||||
return redirect()->route('pos.devices.index')->with('success', 'Device removed.');
|
||||
}
|
||||
|
||||
public function regenerateToken(Request $request, PosDevice $device): RedirectResponse
|
||||
{
|
||||
$this->authorizeOwner($request, $device);
|
||||
$this->devices->regenerateToken($device);
|
||||
|
||||
return back()->with('success', 'Device token regenerated. Re-open the device URL if needed.');
|
||||
}
|
||||
|
||||
/** @return \Illuminate\Support\Collection<int, PosLocation> */
|
||||
protected function locations(Request $request)
|
||||
{
|
||||
$query = PosLocation::owned($this->ownerRef($request))->orderBy('name');
|
||||
$scope = $this->locationScope($request);
|
||||
if ($scope !== null) {
|
||||
$query->whereKey($scope);
|
||||
}
|
||||
|
||||
return $query->get();
|
||||
}
|
||||
|
||||
protected function assertLocationOwned(Request $request, int $locationId): void
|
||||
{
|
||||
$exists = PosLocation::owned($this->ownerRef($request))->whereKey($locationId)->exists();
|
||||
abort_unless($exists, 404);
|
||||
|
||||
$scope = $this->locationScope($request);
|
||||
if ($scope !== null) {
|
||||
abort_unless($locationId === $scope, 404);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user