create(); $this->actingAs($user); // Minimal person/client/case $person = \App\Models\Person\Person::factory()->create([ 'first_name' => 'Job', 'last_name' => 'Runner', 'full_name' => 'Job Runner', ]); $client = Client::create(['person_id' => $person->id]); $case = ClientCase::create([ 'client_id' => $client->id, 'person_id' => $person->id, ]); $returnSeg = Segment::factory()->create(['name' => 'Return']); $fieldSeg = Segment::factory()->create(['name' => 'Field']); $action = Action::create(['name' => 'Call', 'color_tag' => '#000', 'segment_id' => $returnSeg->id]); $decision = Decision::create(['name' => 'Finish field job', 'color_tag' => '#00f', 'auto_mail' => false]); $action->decisions()->attach($decision->id); // Create a contract under the case and attach field segment as active $contract = Contract::create([ 'reference' => 'REF-2002', 'client_case_id' => $case->id, 'type_id' => \Database\Factories\ContractTypeFactory::new()->create()->id, 'start_date' => now()->toDateString(), ]); // Ensure both segments exist on the case and contract DB::table('client_case_segment')->insert([ ['client_case_id' => $case->id, 'segment_id' => $fieldSeg->id, 'active' => true, 'created_at' => now(), 'updated_at' => now()], ['client_case_id' => $case->id, 'segment_id' => $returnSeg->id, 'active' => true, 'created_at' => now(), 'updated_at' => now()], ]); DB::table('contract_segment')->insert([ ['contract_id' => $contract->id, 'segment_id' => $fieldSeg->id, 'active' => true, 'created_at' => now(), 'updated_at' => now()], ['contract_id' => $contract->id, 'segment_id' => $returnSeg->id, 'active' => false, 'created_at' => now(), 'updated_at' => now()], ]); // FieldJobSetting with return segment $setting = FieldJobSetting::create([ 'segment_id' => $fieldSeg->id, 'assign_decision_id' => $decision->id, // not used here 'complete_decision_id' => $decision->id, 'return_segment_id' => $returnSeg->id, 'queue_segment_id' => $fieldSeg->id, 'action_id' => $action->id, ]); // Active FieldJob for this contract (simulates assignment) FieldJob::create([ 'field_job_setting_id' => $setting->id, 'assigned_user_id' => $user->id, 'contract_id' => $contract->id, 'assigned_at' => now()->subDay(), ]); // Insert end_field_job event (satisfy legacy options NOT NULL) $eventId = DB::table('events')->insertGetId([ 'name' => 'End Field Job', 'key' => 'end_field_job', 'description' => 'Complete a field job', 'active' => true, 'options' => json_encode(new stdClass), 'config' => json_encode(new stdClass), 'created_at' => now(), 'updated_at' => now(), ]); DB::table('decision_event')->insert([ 'decision_id' => $decision->id, 'event_id' => $eventId, 'active' => true, 'run_order' => 1, 'config' => json_encode([]), 'created_at' => now(), 'updated_at' => now(), ]); // Spy on logs to assert EndFieldJob ran Log::spy(); $this->post(route('clientCase.activity.store', $case), [ 'action_id' => $action->id, 'decision_id' => $decision->id, 'contract_uuid' => $contract->uuid, 'note' => 'Finish job', ])->assertSessionHasNoErrors(); // The active field job should be marked completed $completedJob = FieldJob::query() ->where('contract_id', $contract->id) ->latest('id') ->first(); expect($completedJob)->not()->toBeNull(); expect($completedJob->completed_at)->not()->toBeNull(); // The contract should now be in return segment $activeSegIds = DB::table('contract_segment') ->where('contract_id', $contract->id) ->where('active', true) ->pluck('segment_id'); expect($activeSegIds->contains($returnSeg->id))->toBeTrue(); });