bootShop('e2e-shop'); $svc = app(WarehouseService::class); // Stock a published part: 20 in inventory. $part = Part::create([ 'name' => 'Plăcuțe BMW', 'article' => 'BMW-001', 'brand' => 'TRW', 'sell_price' => 250, 'buy_price' => 150, 'qty' => 0, 'unit' => 'buc', 'is_active' => true, 'is_published' => true, ]); $svc->receive($part, 20, 150); $this->assertEquals(20.0, (float) $part->fresh()->qty); Mail::fake(); $base = 'http://e2e-shop.service.mir.md'; // 1. Register. $this->post("$base/shop/register", [ 'name' => 'Tester', 'phone' => '+37377012345', 'email' => 'tester@example.com', 'password' => 'secret123', 'password_confirmation' => 'secret123', ])->assertRedirect('/shop/account'); $customer = ShopCustomer::where('phone', '+37377012345')->first(); $this->assertNotNull($customer); $this->assertTrue(Auth::guard('shop')->check()); // 2. Browse catalog (anonymous-safe, but we're logged in). $r = $this->get("$base/shop"); $r->assertOk(); $r->assertSee('Plăcuțe BMW'); // 3. Add to cart. $this->post("$base/shop/part/{$part->id}/add", ['qty' => 2]) ->assertRedirect('/shop/cart'); $this->get("$base/shop/cart")->assertOk()->assertSee('500.00'); // 4. Checkout. $this->get("$base/shop/checkout") ->assertOk() ->assertSee('Tester', false) // prefilled from logged-in customer ->assertSee('+37377012345'); $this->post("$base/shop/checkout", [ 'customer_name' => 'Tester', 'customer_phone' => '+37377012345', 'customer_email' => 'tester@example.com', 'delivery_method' => 'pickup', ])->assertRedirect(); $order = OnlineOrder::first(); $this->assertNotNull($order); $this->assertEquals($customer->id, $order->shop_customer_id); $this->assertEquals(500.0, (float) $order->total); // 5. Confirmation email sent to customer. Mail::assertSent(ShopOrderConfirmationMail::class, fn ($m) => $m->order->id === $order->id); // 6. Tracking page reachable by token (NO auth). Auth::guard('shop')->logout(); $r = $this->get($order->trackingUrl()); $r->assertOk(); $r->assertSee('#' . $order->number); $r->assertSee('Plăcuțe BMW'); // 7. Admin (back in tenant context) fulfills via service::issue. $svc->issue($part, 2, null, $order, 'shop fulfill'); $part->refresh(); $this->assertEquals(18.0, (float) $part->qty, 'stock decreased by 2'); // 8. The warehouse event records the issue with ref pointing at the order. $event = WarehouseEvent::where('part_id', $part->id) ->where('type', 'issue') ->latest('id')->first(); $this->assertNotNull($event); $this->assertEquals(-2.0, (float) $event->qty_delta); $this->assertStringContainsString('OnlineOrder', (string) $event->ref_type); } public function test_guest_checkout_still_works_without_account(): void { $this->bootShop('guest'); $part = Part::create([ 'name' => 'Filtru', 'sell_price' => 50, 'qty' => 5, 'unit' => 'buc', 'is_active' => true, 'is_published' => true, ]); $base = 'http://guest.service.mir.md/shop'; $this->post("$base/part/{$part->id}/add", ['qty' => 1])->assertRedirect(); $this->post("$base/checkout", [ 'customer_name' => 'Guest', 'customer_phone' => '+37399999999', 'delivery_method' => 'pickup', ])->assertRedirect(); $order = OnlineOrder::first(); $this->assertNotNull($order); $this->assertNull($order->shop_customer_id); $this->assertEquals(50.0, (float) $order->total); } private function bootShop(string $slug): array { $plan = Plan::firstOrCreate(['slug' => 'test'], ['name' => 'T', 'price' => 0, 'features' => []]); $company = Company::create([ 'plan_id' => $plan->id, 'slug' => $slug, 'name' => ucfirst($slug), 'status' => 'active', 'settings' => ['shop' => ['enabled' => true, 'delivery_methods' => ['pickup']]], ]); app(TenantManager::class)->setCurrent($company); $wh = Warehouse::create([ 'code' => 'MAIN', 'name' => 'Main', 'is_default' => true, 'is_active' => true, ]); $company->forceFill(['default_warehouse_id' => $wh->id])->saveQuietly(); return compact('company'); } }