'owner-'.uniqid(), 'name' => 'Owner', 'email' => uniqid().'@owner.example.com', ]); } private function locationFor(User $owner): PosLocation { return PosLocation::create([ 'owner_ref' => $owner->public_id, 'name' => 'Front counter', 'currency' => 'GHS', 'is_default' => true, ]); } protected function setUp(): void { parent::setUp(); $this->withoutMiddleware(EnsurePlatformSession::class); $this->withoutVite(); } public function test_operator_can_push_cart_and_public_screen_reads_it(): void { $owner = $this->owner(); $this->locationFor($owner); $this->actingAs($owner) ->postJson(route('pos.customer-display.push'), [ 'phase' => 'cart', 'lines' => [ [ 'name' => 'Blue Shirt', 'quantity' => 2, 'unit_price_minor' => 5000, 'discount_minor' => 500, ], ], 'discount_minor' => 0, 'loyalty' => [ 'label' => 'Rewards', 'points_balance' => 120, 'points_earn' => 9, ], ]) ->assertOk() ->assertJsonPath('phase', 'cart'); $display = PosCustomerDisplay::query()->first(); $this->assertNotNull($display); $this->getJson(route('pos.customer-display.state', ['token' => $display->token])) ->assertOk() ->assertJsonPath('phase', 'cart') ->assertJsonPath('lines.0.name', 'Blue Shirt') ->assertJsonPath('total_minor', 9500) ->assertJsonPath('loyalty.points_balance', 120); } public function test_customer_can_submit_tip_and_signature_actions(): void { $owner = $this->owner(); $location = $this->locationFor($owner); $display = app(CustomerDisplayService::class)->pushCart($location, [ ['name' => 'Lunch', 'quantity' => 1, 'unit_price_minor' => 10000], ], [ 'tip' => [ 'enabled' => true, 'options_percent' => [10, 15, 20], 'custom_allowed' => true, 'base_minor' => 10000, ], ]); app(CustomerDisplayService::class)->push($location, PosCustomerDisplay::PHASE_TIP, [ 'lines' => $display->fresh()->payload['lines'] ?? [], 'total_minor' => 10000, 'tip' => [ 'enabled' => true, 'options_percent' => [10, 15, 20], 'custom_allowed' => true, 'base_minor' => 10000, ], ]); $token = $display->fresh()->token; $this->postJson(route('pos.customer-display.action', ['token' => $token]), [ 'type' => 'tip', 'tip_percent' => 15, 'tip_minor' => 1500, ])->assertOk()->assertJsonPath('ok', true); $this->actingAs($owner) ->getJson(route('pos.customer-display.actions')) ->assertOk() ->assertJsonPath('action.type', 'tip') ->assertJsonPath('action.tip_minor', 1500); $this->postJson(route('pos.customer-display.action', ['token' => $token]), [ 'type' => 'signature', 'signature_data_url' => 'data:image/png;base64,abc', ])->assertOk(); $this->get(route('pos.customer-display.show', ['token' => $token])) ->assertOk() ->assertSee('Order summary', false); } public function test_invalid_token_is_not_found(): void { $this->get(route('pos.customer-display.show', ['token' => str_repeat('a', 48)])) ->assertNotFound(); } public function test_operator_info_returns_display_url(): void { $owner = $this->owner(); $this->locationFor($owner); $this->actingAs($owner) ->getJson(route('pos.customer-display.info')) ->assertOk() ->assertJsonStructure(['url', 'token', 'phase']); } }