user = User::factory()->create([ 'role' => 'service_coordinator', ]); // Give user required permissions for estimates $permissions = [ ['name' => 'service-orders.view', 'display_name' => 'View Service Orders', 'module' => 'service-orders'], ['name' => 'service-orders.create', 'display_name' => 'Create Service Orders', 'module' => 'service-orders'], ['name' => 'service-orders.update', 'display_name' => 'Update Service Orders', 'module' => 'service-orders'], ]; foreach ($permissions as $permissionData) { $permission = \App\Models\Permission::firstOrCreate( ['name' => $permissionData['name']], [ 'display_name' => $permissionData['display_name'], 'description' => $permissionData['display_name'], 'module' => $permissionData['module'], ] ); $this->user->permissions()->attach($permission); } $this->branch = Branch::create([ 'name' => 'Main Branch', 'code' => 'MAIN', 'address' => '123 Test St', 'phone' => '555-0123', 'email' => 'main@test.com', ]); $this->customer = Customer::factory()->create(); $this->vehicle = Vehicle::factory()->create([ 'customer_id' => $this->customer->id, ]); $this->jobCard = JobCard::factory()->create([ 'customer_id' => $this->customer->id, 'vehicle_id' => $this->vehicle->id, 'branch_code' => $this->branch->code, 'status' => 'in_diagnosis', 'service_advisor_id' => $this->user->id, ]); $this->diagnosis = Diagnosis::create([ 'job_card_id' => $this->jobCard->id, 'service_coordinator_id' => $this->user->id, 'diagnostic_findings' => 'Worn brake pads detected', 'labor_operations' => [ [ 'operation' => 'Replace front brake pads', 'estimated_hours' => 2.0, 'labor_rate' => 85.00, ], ], 'parts_required' => [ [ 'part_name' => 'Front brake pads', 'part_number' => 'BP-TOY-001', 'quantity' => 1, 'estimated_cost' => 75.00, ], ], 'estimated_repair_time' => 2.5, 'diagnosis_status' => 'completed', ]); $this->actingAs($this->user); } /** @test */ public function it_can_create_an_estimate_from_diagnosis() { Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]) ->assertSet('diagnosis.id', $this->diagnosis->id) ->assertViewHas('diagnosis') ->call('save') ->assertHasNoErrors() ->assertRedirect(); $this->assertDatabaseHas('estimates', [ 'job_card_id' => $this->jobCard->id, 'diagnosis_id' => $this->diagnosis->id, 'prepared_by_id' => $this->user->id, 'status' => 'draft', ]); // Check that line items were created $estimate = Estimate::latest()->first(); $this->assertGreaterThan(0, $estimate->lineItems()->count()); // Check that totals were calculated $this->assertGreaterThan(0, $estimate->total_amount); } /** @test */ public function it_calculates_totals_correctly() { $component = Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]); // Verify initial line items from diagnosis $lineItems = $component->get('lineItems'); $this->assertNotEmpty($lineItems); // Check that totals are calculated $subtotal = $component->get('subtotal'); $taxAmount = $component->get('tax_amount'); $totalAmount = $component->get('total_amount'); $this->assertGreaterThan(0, $subtotal); $this->assertGreaterThan(0, $taxAmount); $this->assertGreaterThan(0, $totalAmount); // Verify total calculation: subtotal + tax = total $expectedTotal = $subtotal + $taxAmount; $this->assertEquals($expectedTotal, $totalAmount); } /** @test */ public function it_can_add_and_remove_line_items() { $component = Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]); $initialCount = count($component->get('lineItems')); // Add a line item $component->call('addLineItem'); $this->assertCount($initialCount + 1, $component->get('lineItems')); // Remove a line item $component->call('removeLineItem', 0); $this->assertCount($initialCount, $component->get('lineItems')); } /** @test */ public function it_validates_required_fields() { Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]) ->set('terms_and_conditions', '') ->set('validity_period_days', 0) ->set('tax_rate', -1) ->call('save') ->assertHasErrors([ 'terms_and_conditions', 'validity_period_days', 'tax_rate', ]); } /** @test */ public function it_can_view_an_estimate() { $estimate = Estimate::create([ 'estimate_number' => 'MAIN/EST0001', 'job_card_id' => $this->jobCard->id, 'diagnosis_id' => $this->diagnosis->id, 'prepared_by_id' => $this->user->id, 'labor_cost' => 170.00, 'parts_cost' => 90.00, 'subtotal' => 260.00, 'tax_rate' => 8.25, 'tax_amount' => 21.45, 'total_amount' => 281.45, 'validity_period_days' => 30, 'terms_and_conditions' => 'Test terms', 'status' => 'draft', ]); Livewire::test(Show::class, ['estimate' => $estimate]) ->assertSet('estimate.id', $estimate->id) ->assertSee($estimate->estimate_number) ->assertSee('$281.45') ->assertViewHas('estimate'); } /** @test */ public function it_can_edit_an_estimate() { $estimate = Estimate::factory()->create([ 'job_card_id' => $this->jobCard->id, 'terms_and_conditions' => 'Original terms', 'validity_period_days' => 30, ]); // Test the component functionality directly without layout rendering $component = Livewire::test(\App\Livewire\Estimates\Edit::class, ['estimate' => $estimate]) ->set('terms_and_conditions', 'Updated terms and conditions') ->set('validity_period_days', 45); // Assert the properties are set correctly $component->assertSet('terms_and_conditions', 'Updated terms and conditions') ->assertSet('validity_period_days', 45); // Call save method $component->call('save') ->assertHasNoErrors(); $this->assertDatabaseHas('estimates', [ 'id' => $estimate->id, 'terms_and_conditions' => 'Updated terms and conditions', 'validity_period_days' => 45, ]); } /** @test */ public function it_generates_unique_estimate_numbers() { $initialCount = Estimate::count(); // Create first estimate Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]) ->call('save'); $this->assertEquals($initialCount + 1, Estimate::count()); $firstEstimate = Estimate::latest()->first(); // Create another diagnosis for second estimate $diagnosis2 = Diagnosis::create([ 'job_card_id' => $this->jobCard->id, 'service_coordinator_id' => $this->user->id, 'diagnostic_findings' => 'Different issue', 'diagnosis_status' => 'completed', ]); // Create second estimate Livewire::test(Create::class, ['diagnosis' => $diagnosis2]) ->call('save'); $this->assertEquals($initialCount + 2, Estimate::count()); $estimates = Estimate::latest()->take(2)->get(); $secondEstimate = $estimates->first(); $firstEstimate = $estimates->last(); $this->assertNotEquals($firstEstimate->estimate_number, $secondEstimate->estimate_number); $this->assertStringContainsString('MAIN/EST', $firstEstimate->estimate_number); $this->assertStringContainsString('MAIN/EST', $secondEstimate->estimate_number); } /** @test */ public function it_updates_job_card_status_when_estimate_created() { $this->assertEquals('in_diagnosis', $this->jobCard->status); Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]) ->call('save'); $this->jobCard->refresh(); $this->assertEquals('estimate_prepared', $this->jobCard->status); } /** @test */ public function estimate_routes_are_accessible() { // Skip permission checks for this test by using an admin role $this->user->update(['role' => 'admin']); $estimate = Estimate::create([ 'estimate_number' => 'MAIN/EST0001', 'job_card_id' => $this->jobCard->id, 'diagnosis_id' => $this->diagnosis->id, 'prepared_by_id' => $this->user->id, 'labor_cost' => 170.00, 'parts_cost' => 90.00, 'subtotal' => 260.00, 'tax_rate' => 8.25, 'tax_amount' => 21.45, 'total_amount' => 281.45, 'validity_period_days' => 30, 'terms_and_conditions' => 'Test terms', 'status' => 'draft', ]); // Test index route $this->actingAs($this->user) ->get('/estimates') ->assertStatus(200); // Test show route $this->actingAs($this->user) ->get("/estimates/{$estimate->id}") ->assertStatus(200); // Test edit route $this->get("/estimates/{$estimate->id}/edit") ->assertStatus(200); // Test create route $this->get("/estimates/create/{$this->diagnosis->id}") ->assertStatus(200); } /** @test */ public function it_can_calculate_estimate_expiration() { $estimate = Estimate::create([ 'estimate_number' => 'MAIN/EST0001', 'job_card_id' => $this->jobCard->id, 'diagnosis_id' => $this->diagnosis->id, 'prepared_by_id' => $this->user->id, 'labor_cost' => 170.00, 'parts_cost' => 90.00, 'subtotal' => 260.00, 'tax_rate' => 8.25, 'tax_amount' => 21.45, 'total_amount' => 281.45, 'validity_period_days' => 30, 'terms_and_conditions' => 'Test terms', 'status' => 'draft', ]); $expectedValidUntil = $estimate->created_at->addDays(30); $this->assertEquals($expectedValidUntil->format('Y-m-d'), $estimate->valid_until->format('Y-m-d')); // Test if estimate is not expired (fresh estimate) $this->assertFalse($estimate->is_expired); } }