domain = 'test-' . \Illuminate\Support\Str::random(8) . '.erp.local'; $tenantId = 'test_tenant_' . \Illuminate\Support\Str::random(8); // Ensure we are in central context tenancy()->central(function () use ($tenantId) { // Create a tenant $this->tenant = Tenant::create([ 'id' => $tenantId, 'name' => 'Test Tenant', ]); $this->tenant->domains()->create(['domain' => $this->domain]); }); // Initialize to create User and Token tenancy()->initialize($this->tenant); \Artisan::call('tenants:migrate'); $this->user = User::factory()->create([ 'email' => 'admin@test.local', 'name' => 'Admin', ]); $this->token = $this->user->createToken('POS-Test-Token')->plainTextToken; $category = \App\Modules\Inventory\Models\Category::create(['name' => 'General', 'code' => 'GEN']); // Create a product for testing Product::create([ 'name' => 'Existing Product', 'code' => 'P-001', 'external_pos_id' => 'EXT-001', 'sku' => 'SKU-001', 'price' => 100, 'is_active' => true, 'category_id' => $category->id, ]); // End tenancy initialization to simulate external request tenancy()->end(); } protected function tearDown(): void { if ($this->tenant) { $this->tenant->delete(); } parent::tearDown(); } public function test_upsert_product_creates_new_product() { \Laravel\Sanctum\Sanctum::actingAs($this->user, ['*']); $payload = [ 'external_pos_id' => 'EXT-NEW-002', 'name' => 'New Product', 'price' => 200, 'sku' => 'SKU-NEW', ]; $response = $this->withHeaders([ 'X-Tenant-Domain' => $this->domain, 'Accept' => 'application/json', ])->postJson('/api/v1/integration/products/upsert', $payload); $response->assertStatus(200) ->assertJsonPath('message', 'Product synced successfully'); // Verify in Tenant DB tenancy()->initialize($this->tenant); $this->assertDatabaseHas('products', [ 'external_pos_id' => 'EXT-NEW-002', 'name' => 'New Product', ]); tenancy()->end(); } public function test_upsert_product_updates_existing_product() { \Laravel\Sanctum\Sanctum::actingAs($this->user, ['*']); $payload = [ 'external_pos_id' => 'EXT-001', 'name' => 'Updated Name', 'price' => 150, ]; $response = $this->withHeaders([ 'X-Tenant-Domain' => $this->domain, 'Accept' => 'application/json', ])->postJson('/api/v1/integration/products/upsert', $payload); $response->assertStatus(200); tenancy()->initialize($this->tenant); $this->assertDatabaseHas('products', [ 'external_pos_id' => 'EXT-001', 'name' => 'Updated Name', 'price' => 150, ]); tenancy()->end(); } public function test_create_order_deducts_inventory() { // Setup inventory first tenancy()->initialize($this->tenant); $product = Product::where('external_pos_id', 'EXT-001')->first(); // We need a warehouse $warehouse = \App\Modules\Inventory\Models\Warehouse::create(['name' => 'Main Warehouse', 'code' => 'MAIN']); // Add initial stock \App\Modules\Inventory\Models\Inventory::create([ 'product_id' => $product->id, 'warehouse_id' => $warehouse->id, 'quantity' => 100, 'batch_number' => 'BATCH-TEST-001', 'arrival_date' => now()->toDateString(), 'origin_country' => 'TW', ]); $warehouseId = $warehouse->id; tenancy()->end(); \Laravel\Sanctum\Sanctum::actingAs($this->user, ['*']); $payload = [ 'external_order_id' => 'ORD-001', 'warehouse_id' => $warehouseId, 'sold_at' => now()->toIso8601String(), 'items' => [ [ 'pos_product_id' => 'EXT-001', 'qty' => 5, 'price' => 100 ] ] ]; $response = $this->withHeaders([ 'X-Tenant-Domain' => $this->domain, 'Accept' => 'application/json', ])->postJson('/api/v1/integration/orders', $payload); $response->assertStatus(201) ->assertJsonPath('message', 'Order synced and stock deducted successfully'); // Verify Order and Inventory tenancy()->initialize($this->tenant); $this->assertDatabaseHas('sales_orders', [ 'external_order_id' => 'ORD-001', ]); $this->assertDatabaseHas('sales_order_items', [ 'product_id' => $product->id, // We need to fetch ID again or rely on correct ID 'quantity' => 5, ]); // Verify stock deducted: 100 - 5 = 95 $this->assertDatabaseHas('inventories', [ 'product_id' => $product->id, 'warehouse_id' => $warehouseId, 'quantity' => 95, ]); tenancy()->end(); } }