'u-'.uniqid(), 'name' => 'Cashier', 'email' => uniqid().'@example.com', ]); } protected function setUp(): void { parent::setUp(); $this->withoutMiddleware(EnsurePlatformSession::class); $this->withoutVite(); config([ 'crm.url' => 'https://crm.test/api', 'crm.key' => 'test-crm-key', ]); } public function test_barcode_lookup_finds_local_product_in_restaurant_mode(): void { $user = $this->user(); PosLocation::create([ 'owner_ref' => $user->public_id, 'name' => 'Main register', 'currency' => 'GHS', 'service_style' => 'restaurant', ]); PosProduct::create([ 'owner_ref' => $user->public_id, 'name' => 'Water', 'sku' => 'H2O-001', 'price_minor' => 500, 'currency' => 'GHS', 'is_active' => true, ]); $this->actingAs($user) ->getJson(route('pos.register.lookup', ['code' => 'H2O-001'])) ->assertOk() ->assertJsonPath('product.name', 'Water') ->assertJsonPath('product.price_minor', 500); } public function test_barcode_lookup_returns_404_for_unknown_code(): void { $user = $this->user(); PosLocation::create([ 'owner_ref' => $user->public_id, 'name' => 'Main register', 'currency' => 'GHS', 'service_style' => 'restaurant', ]); $this->actingAs($user) ->getJson(route('pos.register.lookup', ['code' => 'MISSING'])) ->assertNotFound(); } public function test_receipt_page_renders_thermal_layout(): void { $user = $this->user(); $location = PosLocation::create([ 'owner_ref' => $user->public_id, 'name' => 'Main register', 'currency' => 'GHS', 'receipt_header' => 'Acme Shop', 'printer_paper_mm' => 58, ]); $sale = PosSale::create([ 'owner_ref' => $user->public_id, 'location_id' => $location->id, 'reference' => 'POS-TEST', 'currency' => 'GHS', 'status' => 'paid', 'payment_method' => 'cash', 'total_minor' => 1000, ]); $sale->lines()->create([ 'name' => 'Tea', 'unit_price_minor' => 1000, 'quantity' => 1, 'line_total_minor' => 1000, ]); $this->actingAs($user) ->get(route('pos.sales.receipt', $sale)) ->assertOk() ->assertSee('Acme Shop') ->assertSee('POS-TEST') ->assertSee('Tea') ->assertSee('Print receipt'); } public function test_cash_sale_redirects_to_receipt_when_auto_print_enabled(): void { Http::fake([ 'crm.test/api/customers*' => Http::response(['data' => []], 200), ]); $user = $this->user(); PosLocation::create([ 'owner_ref' => $user->public_id, 'name' => 'Main register', 'currency' => 'GHS', 'printer_auto_print' => true, ]); $response = $this->actingAs($user)->post(route('pos.register.charge'), [ 'payment_method' => 'cash', 'lines' => [ ['name' => 'Snack', 'unit_price_minor' => 500, 'quantity' => 1], ], ]); $sale = PosSale::where('owner_ref', $user->public_id)->firstOrFail(); $response->assertRedirect(route('pos.sales.receipt', ['sale' => $sale, 'autoprint' => 1])); } public function test_settings_accepts_receipt_logo_upload(): void { Storage::fake('public'); $user = $this->user(); PosLocation::create([ 'owner_ref' => $user->public_id, 'name' => 'Main register', 'currency' => 'GHS', ]); $this->actingAs($user) ->put(route('pos.settings.update'), [ 'name' => 'Main register', 'currency' => 'GHS', 'service_style' => 'retail', 'printer_paper_mm' => 80, 'receipt_logo' => UploadedFile::fake()->image('logo.png', 120, 120), ]) ->assertRedirect(); $location = PosLocation::where('owner_ref', $user->public_id)->firstOrFail(); $this->assertNotNull($location->receipt_logo_path); Storage::disk('public')->assertExists($location->receipt_logo_path); } public function test_settings_saves_payment_gateway_credentials(): void { $user = $this->user(); PosLocation::create([ 'owner_ref' => $user->public_id, 'name' => 'Main register', 'currency' => 'GHS', ]); $this->actingAs($user) ->put(route('pos.settings.update'), [ 'name' => 'Main register', 'currency' => 'GHS', 'service_style' => 'retail', 'printer_paper_mm' => 80, 'gateway_provider' => PaymentGatewaySetting::PROVIDER_PAYSTACK, 'gateway_public_key' => 'pk_test_abc', 'gateway_secret_key' => 'sk_test_xyz', 'gateway_webhook_secret' => 'whsec_test', 'gateway_is_active' => '1', ]) ->assertRedirect() ->assertSessionHas('success'); $setting = PaymentGatewaySetting::query()->where('owner_ref', $user->public_id)->first(); $this->assertNotNull($setting); $this->assertSame(PaymentGatewaySetting::PROVIDER_PAYSTACK, $setting->provider); $this->assertSame('pk_test_abc', $setting->public_key); $this->assertSame('sk_test_xyz', $setting->secret_key); $this->assertSame('whsec_test', $setting->webhook_secret); $this->assertTrue($setting->is_active); $this->assertTrue($setting->isConfigured()); } public function test_settings_keeps_existing_gateway_secrets_when_left_blank(): void { $user = $this->user(); PosLocation::create([ 'owner_ref' => $user->public_id, 'name' => 'Main register', 'currency' => 'GHS', ]); PaymentGatewaySetting::create([ 'owner_ref' => $user->public_id, 'provider' => PaymentGatewaySetting::PROVIDER_PAYSTACK, 'public_key' => 'pk_keep', 'secret_key' => 'sk_keep', 'webhook_secret' => 'whsec_keep', 'is_active' => true, ]); $this->actingAs($user) ->put(route('pos.settings.update'), [ 'name' => 'Main register', 'currency' => 'GHS', 'service_style' => 'retail', 'printer_paper_mm' => 80, 'gateway_provider' => PaymentGatewaySetting::PROVIDER_FLUTTERWAVE, 'gateway_public_key' => '', 'gateway_secret_key' => '', 'gateway_webhook_secret' => '', 'gateway_is_active' => '1', ]) ->assertRedirect() ->assertSessionHas('success'); $setting = PaymentGatewaySetting::query()->where('owner_ref', $user->public_id)->firstOrFail(); $this->assertSame(PaymentGatewaySetting::PROVIDER_FLUTTERWAVE, $setting->provider); $this->assertSame('pk_keep', $setting->public_key); $this->assertSame('sk_keep', $setting->secret_key); $this->assertSame('whsec_keep', $setting->webhook_secret); } public function test_receipt_page_shows_uploaded_logo(): void { Storage::fake('public'); $user = $this->user(); $logoPath = 'pos/receipt-logos/'.$user->public_id.'/logo.png'; Storage::disk('public')->put($logoPath, UploadedFile::fake()->image('logo.png', 120, 120)->getContent()); $location = PosLocation::create([ 'owner_ref' => $user->public_id, 'name' => 'Main register', 'currency' => 'GHS', 'receipt_header' => 'Acme Shop', 'receipt_logo_path' => $logoPath, 'printer_paper_mm' => 80, ]); $sale = PosSale::create([ 'owner_ref' => $user->public_id, 'location_id' => $location->id, 'reference' => 'POS-LOGO', 'currency' => 'GHS', 'status' => 'paid', 'payment_method' => 'cash', 'total_minor' => 1000, ]); $sale->lines()->create([ 'name' => 'Tea', 'unit_price_minor' => 1000, 'quantity' => 1, 'line_total_minor' => 1000, ]); $this->actingAs($user) ->get(route('pos.sales.receipt', $sale)) ->assertOk() ->assertSee('brand-logo', false) ->assertSee('Acme Shop') ->assertSee('POS-LOGO'); } }