'test'], ['name' => 'T', 'price' => 0, 'features' => []]); $this->company = Company::create(['plan_id' => $plan->id, 'slug' => 'mech-' . uniqid(), 'name' => 'M Co', 'status' => 'active']); app(TenantManager::class)->setCurrent($this->company); $this->mechanic = User::create(['name' => 'M', 'email' => 'm@e.com', 'password' => bcrypt('x'), 'role' => 'mechanic', 'status' => 'active']); $client = Client::create(['name' => 'C', 'phone' => '+37399000000', 'type' => 'individual', 'status' => 'active']); $vehicle = Vehicle::create(['client_id' => $client->id, 'make' => 'BMW', 'model' => 'X5', 'plate' => 'MEC-1']); $this->wo = WorkOrder::create([ 'number' => WorkOrder::generateNumber($this->company->id), 'client_id' => $client->id, 'vehicle_id' => $vehicle->id, 'master_id' => $this->mechanic->id, 'opened_at' => today(), 'status' => 'in_work', 'total' => 0, ]); } public function test_start_sets_mechanic_status_in_progress_and_started_at(): void { Carbon::setTestNow('2026-06-05 10:00:00'); $work = WorkOrderWork::create([ 'work_order_id' => $this->wo->id, 'name' => 'Schimb ulei', 'hours' => 1, 'price_per_hour' => 400, ]); $this->assertEquals('pending', $work->mechanic_status); $work->start(); $this->assertEquals('in_progress', $work->mechanic_status); $this->assertEquals('2026-06-05 10:00:00', $work->mechanic_started_at->format('Y-m-d H:i:s')); Carbon::setTestNow(); } public function test_pause_then_resume_accumulates_paused_seconds(): void { Carbon::setTestNow('2026-06-05 10:00:00'); $work = WorkOrderWork::create([ 'work_order_id' => $this->wo->id, 'name' => 'X', 'hours' => 1, 'price_per_hour' => 100, ]); $work->start(); Carbon::setTestNow('2026-06-05 10:30:00'); $work->pause(); $this->assertEquals('paused', $work->mechanic_status); Carbon::setTestNow('2026-06-05 10:45:00'); $work->resume(); $this->assertEquals('in_progress', $work->mechanic_status); $this->assertEqualsWithDelta(15 * 60, $work->paused_seconds_total, 2); Carbon::setTestNow(); } public function test_mark_done_computes_actual_hours_minus_paused(): void { Carbon::setTestNow('2026-06-05 10:00:00'); $work = WorkOrderWork::create([ 'work_order_id' => $this->wo->id, 'name' => 'X', 'hours' => 2, 'price_per_hour' => 200, ]); $work->start(); // Pause 15 min in the middle Carbon::setTestNow('2026-06-05 10:30:00'); $work->pause(); Carbon::setTestNow('2026-06-05 10:45:00'); $work->resume(); // Done at 11:30 → elapsed = 1.5h - 0.25h paused = 1.25h Carbon::setTestNow('2026-06-05 11:30:00'); $work->markDone(); $this->assertEquals('done', $work->mechanic_status); $this->assertEqualsWithDelta(1.25, (float) $work->actual_hours, 0.01); Carbon::setTestNow(); } public function test_block_persists_reason_and_note(): void { $work = WorkOrderWork::create([ 'work_order_id' => $this->wo->id, 'name' => 'X', 'hours' => 1, 'price_per_hour' => 100, ]); $work->start(); $work->block('missing_part', 'Lipsește filtru Mann W712/83'); $work->refresh(); $this->assertEquals('blocked', $work->mechanic_status); $this->assertEquals('missing_part', $work->block_reason); $this->assertEquals('Lipsește filtru Mann W712/83', $work->block_note); } public function test_efficiency_class_thresholds(): void { // norm = 2h $work = WorkOrderWork::create([ 'work_order_id' => $this->wo->id, 'name' => 'X', 'hours' => 2, 'price_per_hour' => 100, ]); $work->actual_hours = 1.8; // faster $this->assertEquals('green', $work->efficiencyClass()); $work->actual_hours = 2.5; // 25% over → green still since 2.5/2 = 1.25 ≤ 1.30 $this->assertEquals('amber', $work->efficiencyClass()); $work->actual_hours = 4.5; // 125% over $this->assertEquals('red', $work->efficiencyClass()); $work->actual_hours = 0; // no actual data $this->assertNull($work->efficiencyClass()); } public function test_invalid_block_reason_is_ignored(): void { $work = WorkOrderWork::create([ 'work_order_id' => $this->wo->id, 'name' => 'X', 'hours' => 1, 'price_per_hour' => 100, ]); $work->block('not_a_valid_reason', 'test'); $work->refresh(); $this->assertNotEquals('blocked', $work->mechanic_status); $this->assertNull($work->block_reason); } public function test_mechanic_board_actions_only_affect_own_works(): void { $other = User::create(['name' => 'O', 'email' => 'o@e.com', 'password' => bcrypt('x'), 'role' => 'mechanic', 'status' => 'active']); $otherWo = WorkOrder::create([ 'number' => WorkOrder::generateNumber($this->company->id), 'master_id' => $other->id, 'opened_at' => today(), 'status' => 'in_work', 'total' => 0, ]); $foreignWork = WorkOrderWork::create([ 'work_order_id' => $otherWo->id, 'name' => "Other's work", 'hours' => 1, 'price_per_hour' => 100, ]); $this->actingAs($this->mechanic); Livewire::test(MechanicBoard::class)->call('startWork', $foreignWork->id); $foreignWork->refresh(); // Stayed pending — board refused to transition $this->assertEquals('pending', $foreignWork->mechanic_status); } public function test_confirm_block_modal_updates_work(): void { $work = WorkOrderWork::create([ 'work_order_id' => $this->wo->id, 'name' => 'X', 'hours' => 1, 'price_per_hour' => 100, ]); $this->actingAs($this->mechanic); Livewire::test(MechanicBoard::class) ->call('openBlockModal', $work->id) ->set('blockReason', 'awaiting_approval') ->set('blockNote', 'Manager n-a răspuns') ->call('confirmBlock'); $work->refresh(); $this->assertEquals('blocked', $work->mechanic_status); $this->assertEquals('awaiting_approval', $work->block_reason); $this->assertEquals('Manager n-a răspuns', $work->block_note); } }