create(); $role = Role::create(['name' => 'Tester', 'slug' => 'tester']); foreach ($perms as $slug) { $p = Permission::firstOrCreate(['slug' => $slug], ['name' => strtoupper(str_replace('-', ' ', $slug))]); $role->permissions()->syncWithoutDetaching($p->id); } $role->users()->syncWithoutDetaching($user->id); return $user; } it('shows reports index with permission', function () { $user = makeUserWithPermissions(['reports-view']); $this->actingAs($user); $resp = $this->get(route('reports.index')); $resp->assertOk(); }); it('counts actions/decisions correctly', function () { $user = makeUserWithPermissions(['reports-view']); $this->actingAs($user); $action = Action::factory()->create(['name' => 'Klic']); $decision = Decision::create(['name' => 'Uspešno']); $group = new PersonGroup; $group->name = 'grp'; $group->save(); $type = PersonType::create(['name' => 'tip']); $person = Person::create(['first_name' => 'Ana', 'last_name' => 'Test', 'full_name' => 'Ana Test', 'group_id' => $group->id, 'type_id' => $type->id]); $client = Client::create(['person_id' => $person->id]); $case = ClientCase::create(['client_id' => $client->id, 'person_id' => $person->id, 'client_ref' => 'X']); Activity::create(['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id]); Activity::create(['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id]); Activity::create(['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id]); $resp = $this->getJson(route('reports.data', 'actions-decisions-counts')); $resp->assertOk(); $json = $resp->json(); expect($json['data'][0]['action_name'])->toBe('Klic'); expect($json['data'][0]['decision_name'])->toBe('Uspešno'); expect($json['data'][0]['activities_count'])->toBe(3); }); it('lists active contracts filtered by client', function () { $user = makeUserWithPermissions(['reports-view']); $this->actingAs($user); // minimal person/client scaffolding $group = new PersonGroup; $group->name = 'grp'; $group->save(); $type = PersonType::create(['name' => 'tip']); $p1 = Person::create(['first_name' => 'Ana', 'last_name' => 'Test', 'full_name' => 'Ana Test', 'group_id' => $group->id, 'type_id' => $type->id]); $p2 = Person::create(['first_name' => 'Bojan', 'last_name' => 'Drugi', 'full_name' => 'Bojan Drugi', 'group_id' => $group->id, 'type_id' => $type->id]); $clientA = App\Models\Client::create(['person_id' => $p1->id]); $clientB = App\Models\Client::create(['person_id' => $p2->id]); $caseA = ClientCase::create(['client_id' => $clientA->id, 'person_id' => $p1->id, 'client_ref' => 'A']); $caseB = ClientCase::create(['client_id' => $clientB->id, 'person_id' => $p2->id, 'client_ref' => 'B']); $ctype = new App\Models\ContractType; $ctype->name = 'Standard'; $ctype->save(); // Active contract for client A App\Models\Contract::create([ 'reference' => 'A-001', 'client_case_id' => $caseA->id, 'type_id' => $ctype->id, 'start_date' => now()->subDays(10)->toDateString(), 'end_date' => null, ]); // Inactive for client A (ended yesterday) App\Models\Contract::create([ 'reference' => 'A-OLD', 'client_case_id' => $caseA->id, 'type_id' => $ctype->id, 'start_date' => now()->subMonths(2)->toDateString(), 'end_date' => now()->subDay()->toDateString(), ]); // Active but for different client B (should be excluded by client filter) App\Models\Contract::create([ 'reference' => 'B-001', 'client_case_id' => $caseB->id, 'type_id' => $ctype->id, 'start_date' => now()->subDays(3)->toDateString(), 'end_date' => null, ]); $resp = $this->getJson(route('reports.data', ['slug' => 'active-contracts', 'client_uuid' => $clientA->uuid, 'as_of' => now()->toDateString()])); $resp->assertOk(); $json = $resp->json(); // Should list exactly the active one for client A expect($json['data'])->toBeArray(); // Filter only references from result $refs = array_map(fn ($r) => $r['contract_reference'] ?? null, $json['data']); expect($refs)->toContain('A-001'); expect($refs)->not->toContain('A-OLD'); expect($refs)->not->toContain('B-001'); }); it('counts activities per segment', function () { $user = makeUserWithPermissions(['reports-view']); $this->actingAs($user); $segment = Segment::create(['name' => 'Prioriteta A', 'description' => 'desc', 'active' => true]); $action = Action::factory()->create(['segment_id' => $segment->id]); $decision = Decision::create(['name' => 'OK']); $group = new PersonGroup; $group->name = 'grp'; $group->save(); $type = PersonType::create(['name' => 'tip']); $person = Person::create(['first_name' => 'Ana', 'last_name' => 'Test', 'full_name' => 'Ana Test', 'group_id' => $group->id, 'type_id' => $type->id]); $client = Client::create(['person_id' => $person->id]); $case = ClientCase::create(['client_id' => $client->id, 'person_id' => $person->id, 'client_ref' => 'X']); Activity::create(['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id]); Activity::create(['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id]); $resp = $this->getJson(route('reports.data', 'segment-activity-counts')); $resp->assertOk(); $json = $resp->json(); expect($json['data'][0]['segment_name'])->toBe('Prioriteta A'); expect($json['data'][0]['activities_count'])->toBe(2); }); it('groups activities per day', function () { $user = makeUserWithPermissions(['reports-view']); $this->actingAs($user); $action = Action::factory()->create(); $decision = Decision::create(['name' => 'OK']); $group = new PersonGroup; $group->name = 'grp'; $group->save(); $type = PersonType::create(['name' => 'tip']); $person = Person::create(['first_name' => 'Ana', 'last_name' => 'Test', 'full_name' => 'Ana Test', 'group_id' => $group->id, 'type_id' => $type->id]); $client = Client::create(['person_id' => $person->id]); $case = ClientCase::create(['client_id' => $client->id, 'person_id' => $person->id, 'client_ref' => 'X']); // Create two today and one yesterday (explicit timestamps via insert to avoid auto-timestamp override) Activity::query()->insert([ ['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id, 'created_at' => now(), 'updated_at' => now()], ['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id, 'created_at' => now(), 'updated_at' => now()], ['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id, 'created_at' => now()->subDay(), 'updated_at' => now()->subDay()], ]); $resp = $this->getJson(route('reports.data', ['slug' => 'activities-per-period', 'period' => 'day'])); $resp->assertOk(); $json = collect($resp->json('data')); // Find today row $todayRow = $json->first(function ($r) { return str_starts_with((string) $r['period'], date('Y-m-d')); }); expect($todayRow)->not()->toBeNull(); expect($todayRow['activities_count'])->toBe(2); }); it('counts activities per decision only', function () { $user = makeUserWithPermissions(['reports-view']); $this->actingAs($user); $decision = Decision::create(['name' => 'OK']); $group = new PersonGroup; $group->name = 'grp'; $group->save(); $type = PersonType::create(['name' => 'tip']); $person = Person::create(['first_name' => 'Ana', 'last_name' => 'Test', 'full_name' => 'Ana Test', 'group_id' => $group->id, 'type_id' => $type->id]); $client = Client::create(['person_id' => $person->id]); $case = ClientCase::create(['client_id' => $client->id, 'person_id' => $person->id, 'client_ref' => 'X']); $action = Action::factory()->create(); Activity::create(['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id]); Activity::create(['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id]); Activity::create(['action_id' => $action->id, 'decision_id' => $decision->id, 'client_case_id' => $case->id]); $resp = $this->getJson(route('reports.data', 'decisions-counts')); $resp->assertOk(); $json = $resp->json(); expect($json['data'][0]['decision_name'])->toBe('OK'); expect($json['data'][0]['activities_count'])->toBe(3); });