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.
173 lines
5.7 KiB
PHP
173 lines
5.7 KiB
PHP
<?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);
|
|
}
|
|
}
|
|
}
|