Deploy Ladill POS / deploy (push) Successful in 56s
Token-gated full-screen customer view shows order, payment QR, tips, signature, receipt, and promo phases while the till keeps operator UI.
123 lines
4.8 KiB
PHP
123 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Pos;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Controllers\Pos\Concerns\ScopesToAccount;
|
|
use App\Models\PosCustomerDisplay;
|
|
use App\Services\Pos\CustomerDisplayService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Validation\Rule;
|
|
|
|
class CustomerDisplayController extends Controller
|
|
{
|
|
use ScopesToAccount;
|
|
|
|
public function __construct(private CustomerDisplayService $displays) {}
|
|
|
|
/** Operator: open/link info for the customer-facing screen. */
|
|
public function info(Request $request): JsonResponse
|
|
{
|
|
$location = $this->location($request);
|
|
$display = $this->displays->ensureForLocation($location);
|
|
|
|
return response()->json([
|
|
'url' => $this->displays->displayUrl($display),
|
|
'token' => $display->token,
|
|
'phase' => $display->phase,
|
|
]);
|
|
}
|
|
|
|
public function rotate(Request $request): JsonResponse
|
|
{
|
|
$location = $this->location($request);
|
|
$display = $this->displays->rotateToken($this->displays->ensureForLocation($location));
|
|
|
|
return response()->json([
|
|
'url' => $this->displays->displayUrl($display),
|
|
'token' => $display->token,
|
|
]);
|
|
}
|
|
|
|
/** Operator: push cart / phase updates from the register. */
|
|
public function push(Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'phase' => ['nullable', 'string', Rule::in(PosCustomerDisplay::PHASES)],
|
|
'lines' => ['nullable', 'array'],
|
|
'lines.*.name' => ['required_with:lines', 'string', 'max:200'],
|
|
'lines.*.quantity' => ['required_with:lines', 'numeric', 'min:0'],
|
|
'lines.*.unit_price_minor' => ['required_with:lines', 'integer', 'min:0'],
|
|
'lines.*.discount_minor' => ['nullable', 'integer', 'min:0'],
|
|
'lines.*.note' => ['nullable', 'string', 'max:200'],
|
|
'discount_minor' => ['nullable', 'integer', 'min:0'],
|
|
'tax_minor' => ['nullable', 'integer', 'min:0'],
|
|
'customer_name' => ['nullable', 'string', 'max:120'],
|
|
'loyalty' => ['nullable', 'array'],
|
|
'loyalty.points_balance' => ['nullable', 'integer'],
|
|
'loyalty.points_earn' => ['nullable', 'integer'],
|
|
'loyalty.points_redeem' => ['nullable', 'integer'],
|
|
'loyalty.label' => ['nullable', 'string', 'max:120'],
|
|
'payment' => ['nullable', 'array'],
|
|
'tip' => ['nullable', 'array'],
|
|
'signature' => ['nullable', 'array'],
|
|
'receipt' => ['nullable', 'array'],
|
|
'promo' => ['nullable', 'array'],
|
|
'message' => ['nullable', 'string', 'max:500'],
|
|
'sale_reference' => ['nullable', 'string', 'max:64'],
|
|
]);
|
|
|
|
$location = $this->location($request);
|
|
$phase = $data['phase'] ?? null;
|
|
$lines = $data['lines'] ?? null;
|
|
|
|
$extras = array_filter([
|
|
'discount_minor' => $data['discount_minor'] ?? null,
|
|
'tax_minor' => $data['tax_minor'] ?? null,
|
|
'customer_name' => $data['customer_name'] ?? null,
|
|
'loyalty' => $data['loyalty'] ?? null,
|
|
'payment' => $data['payment'] ?? null,
|
|
'tip' => $data['tip'] ?? null,
|
|
'signature' => $data['signature'] ?? null,
|
|
'receipt' => $data['receipt'] ?? null,
|
|
'promo' => $data['promo'] ?? null,
|
|
'message' => $data['message'] ?? null,
|
|
'sale_reference' => $data['sale_reference'] ?? null,
|
|
], fn ($v) => $v !== null);
|
|
|
|
if ($phase === PosCustomerDisplay::PHASE_CART || ($lines !== null && $phase === null)) {
|
|
$display = $this->displays->pushCart($location, $lines ?? [], $extras);
|
|
} elseif ($phase !== null) {
|
|
if ($lines !== null) {
|
|
$extras['lines'] = $lines;
|
|
}
|
|
$display = $this->displays->push($location, $phase, $extras);
|
|
} else {
|
|
$display = $this->displays->push($location, PosCustomerDisplay::PHASE_IDLE, $extras);
|
|
}
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'phase' => $display->phase,
|
|
'updated_at' => optional($display->last_pushed_at)->toIso8601String(),
|
|
]);
|
|
}
|
|
|
|
/** Operator: read (and optionally clear) the latest customer action (tip, signature, email). */
|
|
public function actions(Request $request): JsonResponse
|
|
{
|
|
$location = $this->location($request);
|
|
$display = $this->displays->ensureForLocation($location);
|
|
$action = $display->customer_action;
|
|
|
|
if ($request->boolean('clear') && $action) {
|
|
$this->displays->clearCustomerAction($display);
|
|
}
|
|
|
|
return response()->json([
|
|
'action' => $action,
|
|
]);
|
|
}
|
|
}
|