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, ]); } }